Merge remote-tracking branch 'td/master'

This commit is contained in:
Andrea Cavalli 2022-09-22 00:39:18 +02:00
commit 0bfef9e46f
143 changed files with 5206 additions and 1626 deletions

View File

@ -6,7 +6,7 @@ if (POLICY CMP0065)
cmake_policy(SET CMP0065 NEW)
endif()
project(TDLib VERSION 1.8.5 LANGUAGES CXX C)
project(TDLib VERSION 1.8.6 LANGUAGES CXX C)
if (NOT DEFINED CMAKE_MODULE_PATH)
set(CMAKE_MODULE_PATH "")
@ -293,7 +293,6 @@ set(TDLIB_SOURCE
td/telegram/AudiosManager.cpp
td/telegram/AuthManager.cpp
td/telegram/AutoDownloadSettings.cpp
td/telegram/AvailableReaction.cpp
td/telegram/BackgroundManager.cpp
td/telegram/BackgroundType.cpp
td/telegram/BotCommand.cpp
@ -305,6 +304,7 @@ set(TDLIB_SOURCE
td/telegram/CallManager.cpp
td/telegram/CallbackQueriesManager.cpp
td/telegram/ChannelParticipantFilter.cpp
td/telegram/ChatReactions.cpp
td/telegram/ClientActor.cpp
td/telegram/ConfigManager.cpp
td/telegram/ConnectionState.cpp
@ -333,6 +333,8 @@ set(TDLIB_SOURCE
td/telegram/DownloadManager.cpp
td/telegram/DownloadManagerCallback.cpp
td/telegram/DraftMessage.cpp
td/telegram/EmailVerification.cpp
td/telegram/EmojiStatus.cpp
td/telegram/FileReferenceManager.cpp
td/telegram/files/FileBitmask.cpp
td/telegram/files/FileDb.cpp
@ -431,6 +433,7 @@ set(TDLIB_SOURCE
td/telegram/SecureStorage.cpp
td/telegram/SecureValue.cpp
td/telegram/SendCodeHelper.cpp
td/telegram/SentEmailCode.cpp
td/telegram/SequenceDispatcher.cpp
td/telegram/SpecialStickerSetType.cpp
td/telegram/SponsoredMessageManager.cpp
@ -494,7 +497,6 @@ set(TDLIB_SOURCE
td/telegram/AudiosManager.h
td/telegram/AuthManager.h
td/telegram/AutoDownloadSettings.h
td/telegram/AvailableReaction.h
td/telegram/BackgroundId.h
td/telegram/BackgroundManager.h
td/telegram/BackgroundType.h
@ -510,6 +512,7 @@ set(TDLIB_SOURCE
td/telegram/ChannelParticipantFilter.h
td/telegram/ChannelType.h
td/telegram/ChatId.h
td/telegram/ChatReactions.h
td/telegram/ClientActor.h
td/telegram/ConfigManager.h
td/telegram/ConnectionState.h
@ -542,6 +545,8 @@ set(TDLIB_SOURCE
td/telegram/DownloadManager.h
td/telegram/DownloadManagerCallback.h
td/telegram/DraftMessage.h
td/telegram/EmailVerification.h
td/telegram/EmojiStatus.h
td/telegram/EncryptedFile.h
td/telegram/FileReferenceManager.h
td/telegram/files/FileBitmask.h
@ -677,6 +682,7 @@ set(TDLIB_SOURCE
td/telegram/SecureStorage.h
td/telegram/SecureValue.h
td/telegram/SendCodeHelper.h
td/telegram/SentEmailCode.h
td/telegram/SequenceDispatcher.h
td/telegram/ServerMessageId.h
td/telegram/SetWithPosition.h

View File

@ -130,7 +130,7 @@ target_link_libraries(YourTarget PRIVATE Td::TdStatic)
Or you could install `TDLib` and then reference it in your CMakeLists.txt like this:
```
find_package(Td 1.8.5 REQUIRED)
find_package(Td 1.8.6 REQUIRED)
target_link_libraries(YourTarget PRIVATE Td::TdStatic)
```
See [example/cpp/CMakeLists.txt](https://github.com/tdlight-team/tdlight/blob/master/example/cpp/CMakeLists.txt).

View File

@ -46,10 +46,9 @@ class ActorTraits<TestActor> {
} // namespace td
class CreateActorBench final : public td::Benchmark {
td::ConcurrentScheduler scheduler_;
td::ConcurrentScheduler scheduler_{0, 0};
void start_up() final {
scheduler_.init(0);
scheduler_.start();
}
@ -140,8 +139,7 @@ class RingBench final : public td::Benchmark {
}
void start_up() final {
scheduler_ = new td::ConcurrentScheduler();
scheduler_->init(thread_n_);
scheduler_ = new td::ConcurrentScheduler(thread_n_, 0);
actor_array_ = td::vector<td::ActorId<PassActor>>(actor_n_);
for (int i = 0; i < actor_n_; i++) {
@ -293,8 +291,7 @@ class QueryBench final : public td::Benchmark {
};
void start_up() final {
scheduler_ = new td::ConcurrentScheduler();
scheduler_->init(0);
scheduler_ = new td::ConcurrentScheduler(0, 0);
server_ = scheduler_->create_actor_unsafe<ServerActor>(0, "Server");
scheduler_->start();

View File

@ -52,6 +52,106 @@ class SHA1Bench final : public td::Benchmark {
};
#endif
class SHA1ShortBench final : public td::Benchmark {
public:
alignas(64) unsigned char data[SHORT_DATA_SIZE];
std::string get_description() const final {
return PSTRING() << "SHA1 [" << SHORT_DATA_SIZE << "B]";
}
void start_up() final {
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
}
void run(int n) final {
unsigned char md[20];
for (int i = 0; i < n; i++) {
td::sha1(td::Slice(data, SHORT_DATA_SIZE), md);
}
}
};
class SHA256ShortBench final : public td::Benchmark {
public:
alignas(64) unsigned char data[SHORT_DATA_SIZE];
std::string get_description() const final {
return PSTRING() << "SHA256 [" << SHORT_DATA_SIZE << "B]";
}
void start_up() final {
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
}
void run(int n) final {
unsigned char md[32];
for (int i = 0; i < n; i++) {
td::sha256(td::Slice(data, SHORT_DATA_SIZE), td::MutableSlice(md, 32));
}
}
};
class SHA512ShortBench final : public td::Benchmark {
public:
alignas(64) unsigned char data[SHORT_DATA_SIZE];
std::string get_description() const final {
return PSTRING() << "SHA512 [" << SHORT_DATA_SIZE << "B]";
}
void start_up() final {
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
}
void run(int n) final {
unsigned char md[64];
for (int i = 0; i < n; i++) {
td::sha512(td::Slice(data, SHORT_DATA_SIZE), td::MutableSlice(md, 64));
}
}
};
class HmacSha256ShortBench final : public td::Benchmark {
public:
alignas(64) unsigned char data[SHORT_DATA_SIZE];
std::string get_description() const final {
return PSTRING() << "HMAC-SHA256 [" << SHORT_DATA_SIZE << "B]";
}
void start_up() final {
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
}
void run(int n) final {
unsigned char md[32];
for (int i = 0; i < n; i++) {
td::hmac_sha256(td::Slice(data, SHORT_DATA_SIZE), td::Slice(data, SHORT_DATA_SIZE), td::MutableSlice(md, 32));
}
}
};
class HmacSha512ShortBench final : public td::Benchmark {
public:
alignas(64) unsigned char data[SHORT_DATA_SIZE];
std::string get_description() const final {
return PSTRING() << "HMAC-SHA512 [" << SHORT_DATA_SIZE << "B]";
}
void start_up() final {
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
}
void run(int n) final {
unsigned char md[32];
for (int i = 0; i < n; i++) {
td::hmac_sha256(td::Slice(data, SHORT_DATA_SIZE), td::Slice(data, SHORT_DATA_SIZE), td::MutableSlice(md, 32));
}
}
};
class AesEcbBench final : public td::Benchmark {
public:
alignas(64) unsigned char data[DATA_SIZE];
@ -415,6 +515,11 @@ int main() {
#if OPENSSL_VERSION_NUMBER <= 0x10100000L
td::bench(SHA1Bench());
#endif
td::bench(SHA1ShortBench());
td::bench(SHA256ShortBench());
td::bench(SHA512ShortBench());
td::bench(HmacSha256ShortBench());
td::bench(HmacSha512ShortBench());
td::bench(Crc32Bench());
td::bench(Crc64Bench());
}

View File

@ -29,7 +29,7 @@
template <class KeyValueT>
class TdKvBench final : public td::Benchmark {
td::ConcurrentScheduler sched;
td::ConcurrentScheduler sched{1, 0};
td::string name_;
public:
@ -72,7 +72,6 @@ class TdKvBench final : public td::Benchmark {
};
void start_up_n(int n) final {
sched.init(1);
sched.create_actor_unsafe<Main>(1, "Main", n).release();
}
@ -179,8 +178,7 @@ class SqliteKeyValueAsyncBench final : public td::Benchmark {
td::unique_ptr<td::SqliteKeyValueAsyncInterface> sqlite_kv_async_;
td::Status do_start_up() {
scheduler_ = td::make_unique<td::ConcurrentScheduler>();
scheduler_->init(1);
scheduler_ = td::make_unique<td::ConcurrentScheduler>(1, 0);
auto guard = scheduler_->get_main_guard();

View File

@ -66,8 +66,7 @@ class HttpClient final : public td::HttpOutboundConnection::Callback {
int main() {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
auto scheduler = td::make_unique<td::ConcurrentScheduler>();
scheduler->init(0);
auto scheduler = td::make_unique<td::ConcurrentScheduler>(0, 0);
scheduler->create_actor_unsafe<HttpClient>(0, "Client1").release();
scheduler->create_actor_unsafe<HttpClient>(0, "Client2").release();
scheduler->start();

View File

@ -74,8 +74,7 @@ class Server final : public td::TcpListener::Callback {
int main() {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
auto scheduler = td::make_unique<td::ConcurrentScheduler>();
scheduler->init(N);
auto scheduler = td::make_unique<td::ConcurrentScheduler>(N, 0);
scheduler->create_actor_unsafe<Server>(0, "Server").release();
scheduler->start();
while (scheduler->run_main(10)) {

View File

@ -121,8 +121,7 @@ class Server final : public td::TcpListener::Callback {
int main() {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
auto scheduler = td::make_unique<td::ConcurrentScheduler>();
scheduler->init(N);
auto scheduler = td::make_unique<td::ConcurrentScheduler>(N, 0);
scheduler->create_actor_unsafe<Server>(0, "Server").release();
scheduler->start();
while (scheduler->run_main(10)) {

View File

@ -106,8 +106,7 @@ class Server final : public td::TcpListener::Callback {
int main() {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
auto scheduler = td::make_unique<td::ConcurrentScheduler>();
scheduler->init(N);
auto scheduler = td::make_unique<td::ConcurrentScheduler>(N, 0);
scheduler->create_actor_unsafe<Server>(0, "Server").release();
scheduler->start();
while (scheduler->run_main(10)) {

View File

@ -87,8 +87,7 @@ class MessagesDbBench final : public td::Benchmark {
std::shared_ptr<td::MessagesDbAsyncInterface> messages_db_async_;
td::Status do_start_up() {
scheduler_ = td::make_unique<td::ConcurrentScheduler>();
scheduler_->init(1);
scheduler_ = td::make_unique<td::ConcurrentScheduler>(1, 0);
auto guard = scheduler_->get_main_guard();

View File

@ -23,8 +23,7 @@ int main(int argc, char *argv[]) {
auto timeout = 10;
auto ttl = 3;
auto prefer_ipv6 = (argc > 2 && td::string(argv[2]) == "-6");
auto scheduler = td::make_unique<td::ConcurrentScheduler>();
scheduler->init(0);
auto scheduler = td::make_unique<td::ConcurrentScheduler>(0, 0);
scheduler
->create_actor_unsafe<td::Wget>(0, "Client",
td::PromiseCreator::lambda([](td::Result<td::unique_ptr<td::HttpQuery>> res) {

View File

@ -746,7 +746,7 @@ function onOptionsChanged() {
pre_text.push('Note that building requires a lot of memory, so you may need to increase allowed per-process memory usage in /etc/login.conf or build from root.');
}
if (os_netbsd) {
pre_text.push('Note that the following instruction is for NetBSD 8.0 and default SH shell.');
pre_text.push('Note that the following instruction is for NetBSD 8+ and default SH shell.');
}
if (os_mac) {
pre_text.push('Note that the following instruction will build TDLight only for ' + os_mac_host_name + '.');
@ -917,8 +917,8 @@ function onOptionsChanged() {
if (!use_root) {
commands.push('su -');
}
commands.push('export PKG_PATH=ftp://ftp.netbsd.org/pub/pkgsrc/packages/NetBSD/i386/8.0_2019Q2/All');
var packages = 'git gperf php-7.3.6 cmake openssl gcc5-libs';
commands.push('export PKG_PATH=http://cdn.netbsd.org/pub/pkgsrc/packages/NetBSD/$(uname -p)/$(uname -r)/All');
var packages = 'git gperf pcre2 php-8* cmake openssl gcc12-libs mozilla-rootcerts-openssl';
commands.push('pkg_add ' + packages);
if (!use_root) {
commands.push('exit');

View File

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

View File

@ -238,82 +238,85 @@ class TdExample {
void on_authorization_state_update() {
authentication_query_id_++;
td_api::downcast_call(
*authorization_state_,
overloaded(
[this](td_api::authorizationStateReady &) {
are_authorized_ = true;
std::cout << "Got authorization" << std::endl;
},
[this](td_api::authorizationStateLoggingOut &) {
are_authorized_ = false;
std::cout << "Logging out" << std::endl;
},
[this](td_api::authorizationStateClosing &) { std::cout << "Closing" << std::endl; },
[this](td_api::authorizationStateClosed &) {
are_authorized_ = false;
need_restart_ = true;
std::cout << "Terminated" << std::endl;
},
[this](td_api::authorizationStateWaitCode &) {
std::cout << "Enter authentication code: " << std::flush;
std::string code;
std::cin >> code;
send_query(td_api::make_object<td_api::checkAuthenticationCode>(code),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitRegistration &) {
std::string first_name;
std::string last_name;
std::cout << "Enter your first name: " << std::flush;
std::cin >> first_name;
std::cout << "Enter your last name: " << std::flush;
std::cin >> last_name;
send_query(td_api::make_object<td_api::registerUser>(first_name, last_name),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitPassword &) {
std::cout << "Enter authentication password: " << std::flush;
std::string password;
std::getline(std::cin, password);
send_query(td_api::make_object<td_api::checkAuthenticationPassword>(password),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitOtherDeviceConfirmation &state) {
std::cout << "Confirm this login link on another device: " << state.link_ << std::endl;
},
[this](td_api::authorizationStateWaitPhoneNumber &) {
std::cout << "Enter phone number: " << std::flush;
std::string phone_number;
std::cin >> phone_number;
send_query(td_api::make_object<td_api::setAuthenticationPhoneNumber>(phone_number, nullptr),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitEncryptionKey &) {
std::cout << "Enter encryption key or DESTROY: " << std::flush;
std::string key;
std::getline(std::cin, key);
if (key == "DESTROY") {
send_query(td_api::make_object<td_api::destroy>(), create_authentication_query_handler());
} else {
send_query(td_api::make_object<td_api::checkDatabaseEncryptionKey>(std::move(key)),
create_authentication_query_handler());
}
},
[this](td_api::authorizationStateWaitTdlibParameters &) {
auto parameters = td_api::make_object<td_api::tdlibParameters>();
parameters->database_directory_ = "tdlib";
parameters->use_message_database_ = true;
parameters->use_secret_chats_ = true;
parameters->api_id_ = 94575;
parameters->api_hash_ = "a3406de8d171bb422bb6ddf3bbd800e2";
parameters->system_language_code_ = "en";
parameters->device_model_ = "Desktop";
parameters->application_version_ = "1.0";
parameters->enable_storage_optimizer_ = true;
send_query(td_api::make_object<td_api::setTdlibParameters>(std::move(parameters)),
create_authentication_query_handler());
}));
td_api::downcast_call(*authorization_state_,
overloaded(
[this](td_api::authorizationStateReady &) {
are_authorized_ = true;
std::cout << "Got authorization" << std::endl;
},
[this](td_api::authorizationStateLoggingOut &) {
are_authorized_ = false;
std::cout << "Logging out" << std::endl;
},
[this](td_api::authorizationStateClosing &) { std::cout << "Closing" << std::endl; },
[this](td_api::authorizationStateClosed &) {
are_authorized_ = false;
need_restart_ = true;
std::cout << "Terminated" << std::endl;
},
[this](td_api::authorizationStateWaitPhoneNumber &) {
std::cout << "Enter phone number: " << std::flush;
std::string phone_number;
std::cin >> phone_number;
send_query(
td_api::make_object<td_api::setAuthenticationPhoneNumber>(phone_number, nullptr),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitEmailAddress &) {
std::cout << "Enter email address: " << std::flush;
std::string email_address;
std::cin >> email_address;
send_query(td_api::make_object<td_api::setAuthenticationEmailAddress>(email_address),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitEmailCode &) {
std::cout << "Enter email authentication code: " << std::flush;
std::string code;
std::cin >> code;
send_query(td_api::make_object<td_api::checkAuthenticationEmailCode>(
td_api::make_object<td_api::emailAddressAuthenticationCode>(code)),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitCode &) {
std::cout << "Enter authentication code: " << std::flush;
std::string code;
std::cin >> code;
send_query(td_api::make_object<td_api::checkAuthenticationCode>(code),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitRegistration &) {
std::string first_name;
std::string last_name;
std::cout << "Enter your first name: " << std::flush;
std::cin >> first_name;
std::cout << "Enter your last name: " << std::flush;
std::cin >> last_name;
send_query(td_api::make_object<td_api::registerUser>(first_name, last_name),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitPassword &) {
std::cout << "Enter authentication password: " << std::flush;
std::string password;
std::getline(std::cin, password);
send_query(td_api::make_object<td_api::checkAuthenticationPassword>(password),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitOtherDeviceConfirmation &state) {
std::cout << "Confirm this login link on another device: " << state.link_ << std::endl;
},
[this](td_api::authorizationStateWaitTdlibParameters &) {
auto request = td_api::make_object<td_api::setTdlibParameters>();
request->database_directory_ = "tdlib";
request->use_message_database_ = true;
request->use_secret_chats_ = true;
request->api_id_ = 94575;
request->api_hash_ = "a3406de8d171bb422bb6ddf3bbd800e2";
request->system_language_code_ = "en";
request->device_model_ = "Desktop";
request->application_version_ = "1.0";
request->enable_storage_optimizer_ = true;
send_query(std::move(request), create_authentication_query_handler());
}));
}
void check_authentication_error(Object object) {

View File

@ -67,28 +67,34 @@ namespace TdExample
}
if (_authorizationState is TdApi.AuthorizationStateWaitTdlibParameters)
{
TdApi.TdlibParameters parameters = new TdApi.TdlibParameters();
parameters.DatabaseDirectory = "tdlib";
parameters.UseMessageDatabase = true;
parameters.UseSecretChats = true;
parameters.ApiId = 94575;
parameters.ApiHash = "a3406de8d171bb422bb6ddf3bbd800e2";
parameters.SystemLanguageCode = "en";
parameters.DeviceModel = "Desktop";
parameters.ApplicationVersion = "1.0";
parameters.EnableStorageOptimizer = true;
TdApi.SetTdlibParameters request = new TdApi.SetTdlibParameters();
request.DatabaseDirectory = "tdlib";
request.UseMessageDatabase = true;
request.UseSecretChats = true;
request.ApiId = 94575;
request.ApiHash = "a3406de8d171bb422bb6ddf3bbd800e2";
request.SystemLanguageCode = "en";
request.DeviceModel = "Desktop";
request.ApplicationVersion = "1.0";
request.EnableStorageOptimizer = true;
_client.Send(new TdApi.SetTdlibParameters(parameters), new AuthorizationRequestHandler());
}
else if (_authorizationState is TdApi.AuthorizationStateWaitEncryptionKey)
{
_client.Send(new TdApi.CheckDatabaseEncryptionKey(), new AuthorizationRequestHandler());
_client.Send(request, new AuthorizationRequestHandler());
}
else if (_authorizationState is TdApi.AuthorizationStateWaitPhoneNumber)
{
string phoneNumber = ReadLine("Please enter phone number: ");
_client.Send(new TdApi.SetAuthenticationPhoneNumber(phoneNumber, null), new AuthorizationRequestHandler());
}
else if (_authorizationState is TdApi.AuthorizationStateWaitEmailAddress)
{
string emailAddress = ReadLine("Please enter email address: ");
_client.Send(new TdApi.SetAuthenticationEmailAddress(emailAddress), new AuthorizationRequestHandler());
}
else if (_authorizationState is TdApi.AuthorizationStateWaitEmailCode)
{
string code = ReadLine("Please enter email authentication code: ");
_client.Send(new TdApi.CheckAuthenticationEmailCode(new TdApi.EmailAddressAuthenticationCode(code)), new AuthorizationRequestHandler());
}
else if (_authorizationState is TdApi.AuthorizationStateWaitOtherDeviceConfirmation state)
{
Console.WriteLine("Please confirm this login link on another device: " + state.Link);

View File

@ -13,6 +13,14 @@ import java.util.concurrent.atomic.AtomicLong;
* Main class for interaction with the TDLib.
*/
public final class Client {
static {
try {
System.loadLibrary("tdjni");
} catch (UnsatisfiedLinkError e) {
e.printStackTrace();
}
}
/**
* Interface for handler for results of queries to TDLib and incoming updates from TDLib.
*/

View File

@ -55,14 +55,6 @@ public final class Example {
private static final String commandsLine = "Enter command (gcs - GetChats, gc <chatId> - GetChat, me - GetMe, sm <chatId> <message> - SendMessage, lo - LogOut, q - Quit): ";
private static volatile String currentPrompt = null;
static {
try {
System.loadLibrary("tdjni");
} catch (UnsatisfiedLinkError e) {
e.printStackTrace();
}
}
private static void print(String str) {
if (currentPrompt != null) {
System.out.println("");
@ -101,21 +93,18 @@ public final class Example {
}
switch (Example.authorizationState.getConstructor()) {
case TdApi.AuthorizationStateWaitTdlibParameters.CONSTRUCTOR:
TdApi.TdlibParameters parameters = new TdApi.TdlibParameters();
parameters.databaseDirectory = "tdlib";
parameters.useMessageDatabase = true;
parameters.useSecretChats = true;
parameters.apiId = 94575;
parameters.apiHash = "a3406de8d171bb422bb6ddf3bbd800e2";
parameters.systemLanguageCode = "en";
parameters.deviceModel = "Desktop";
parameters.applicationVersion = "1.0";
parameters.enableStorageOptimizer = true;
TdApi.SetTdlibParameters request = new TdApi.SetTdlibParameters();
request.databaseDirectory = "tdlib";
request.useMessageDatabase = true;
request.useSecretChats = true;
request.apiId = 94575;
request.apiHash = "a3406de8d171bb422bb6ddf3bbd800e2";
request.systemLanguageCode = "en";
request.deviceModel = "Desktop";
request.applicationVersion = "1.0";
request.enableStorageOptimizer = true;
client.send(new TdApi.SetTdlibParameters(parameters), new AuthorizationRequestHandler());
break;
case TdApi.AuthorizationStateWaitEncryptionKey.CONSTRUCTOR:
client.send(new TdApi.CheckDatabaseEncryptionKey(), new AuthorizationRequestHandler());
client.send(request, new AuthorizationRequestHandler());
break;
case TdApi.AuthorizationStateWaitPhoneNumber.CONSTRUCTOR: {
String phoneNumber = promptString("Please enter phone number: ");
@ -127,6 +116,16 @@ public final class Example {
System.out.println("Please confirm this login link on another device: " + link);
break;
}
case TdApi.AuthorizationStateWaitEmailAddress.CONSTRUCTOR: {
String emailAddress = promptString("Please enter email address: ");
client.send(new TdApi.SetAuthenticationEmailAddress(emailAddress), new AuthorizationRequestHandler());
break;
}
case TdApi.AuthorizationStateWaitEmailCode.CONSTRUCTOR: {
String code = promptString("Please enter email authentication code: ");
client.send(new TdApi.CheckAuthenticationEmailCode(new TdApi.EmailAddressAuthenticationCode(code)), new AuthorizationRequestHandler());
break;
}
case TdApi.AuthorizationStateWaitCode.CONSTRUCTOR: {
String code = promptString("Please enter authentication code: ");
client.send(new TdApi.CheckAuthenticationCode(code), new AuthorizationRequestHandler());

View File

@ -94,26 +94,33 @@ while True:
# you MUST obtain your own api_id and api_hash at https://my.telegram.org
# and use them in the setTdlibParameters call
if auth_state['@type'] == 'authorizationStateWaitTdlibParameters':
td_send({'@type': 'setTdlibParameters', 'parameters': {
'database_directory': 'tdlib',
'use_message_database': True,
'use_secret_chats': True,
'api_id': 94575,
'api_hash': 'a3406de8d171bb422bb6ddf3bbd800e2',
'system_language_code': 'en',
'device_model': 'Desktop',
'application_version': '1.0',
'enable_storage_optimizer': True}})
# set an encryption key for database to let know TDLib how to open the database
if auth_state['@type'] == 'authorizationStateWaitEncryptionKey':
td_send({'@type': 'checkDatabaseEncryptionKey', 'encryption_key': ''})
td_send({'@type': 'setTdlibParameters',
'database_directory': 'tdlib',
'use_message_database': True,
'use_secret_chats': True,
'api_id': 94575,
'api_hash': 'a3406de8d171bb422bb6ddf3bbd800e2',
'system_language_code': 'en',
'device_model': 'Desktop',
'application_version': '1.0',
'enable_storage_optimizer': True})
# enter phone number to log in
if auth_state['@type'] == 'authorizationStateWaitPhoneNumber':
phone_number = input('Please enter your phone number: ')
td_send({'@type': 'setAuthenticationPhoneNumber', 'phone_number': phone_number})
# enter email address to log in
if auth_state['@type'] == 'authorizationStateWaitEmailAddress':
email_address = input('Please enter your email address: ')
td_send({'@type': 'setAuthenticationEmailAddress', 'email_address': email_address})
# wait for email authorization code
if auth_state['@type'] == 'authorizationStateWaitEmailCode':
code = input('Please enter the email authentication code you received: ')
td_send({'@type': 'checkAuthenticationEmailCode',
'code': {'@type': 'emailAddressAuthenticationCode', 'code' : 'code'}})
# wait for authorization code
if auth_state['@type'] == 'authorizationStateWaitCode':
code = input('Please enter the authentication code you received: ')

View File

@ -107,27 +107,33 @@ func updateAuthorizationState(authorizationState: Dictionary<String, Any>) {
case "authorizationStateWaitTdlibParameters":
client.queryAsync(query:[
"@type":"setTdlibParameters",
"parameters":[
"database_directory":"tdlib",
"use_message_database":true,
"use_secret_chats":true,
"api_id":94575,
"api_hash":"a3406de8d171bb422bb6ddf3bbd800e2",
"system_language_code":"en",
"device_model":"Desktop",
"application_version":"1.0",
"enable_storage_optimizer":true
]
"database_directory":"tdlib",
"use_message_database":true,
"use_secret_chats":true,
"api_id":94575,
"api_hash":"a3406de8d171bb422bb6ddf3bbd800e2",
"system_language_code":"en",
"device_model":"Desktop",
"application_version":"1.0",
"enable_storage_optimizer":true
]);
case "authorizationStateWaitEncryptionKey":
client.queryAsync(query: ["@type":"checkDatabaseEncryptionKey", "encryption_key":""])
case "authorizationStateWaitPhoneNumber":
print("Enter your phone number: ")
let phone_number = myReadLine()
client.queryAsync(query:["@type":"setAuthenticationPhoneNumber", "phone_number":phone_number], f:checkAuthenticationError)
case "authorizationStateWaitEmailAddress":
print("Enter your email address: ")
let email_address = myReadLine()
client.queryAsync(query:["@type":"setAuthenticationEmailAddress", "email_address":email_address], f:checkAuthenticationError)
case "authorizationStateWaitEmailCode":
var code: String = ""
print("Enter email code: ")
code = myReadLine()
client.queryAsync(query:["@type":"checkAuthenticationEmailCode", "code":["@type":"emailAddressAuthenticationCode", "code":code]], f:checkAuthenticationError)
case "authorizationStateWaitCode":
var code: String = ""
print("Enter (SMS) code: ")

View File

@ -36,17 +36,16 @@ namespace TdApp
});
_client = Td.Client.Create(_handler);
var parameters = new TdApi.TdlibParameters();
parameters.DatabaseDirectory = Windows.Storage.ApplicationData.Current.LocalFolder.Path;
parameters.UseSecretChats = true;
parameters.UseMessageDatabase = true;
parameters.ApiId = 94575;
parameters.ApiHash = "a3406de8d171bb422bb6ddf3bbd800e2";
parameters.SystemLanguageCode = "en";
parameters.DeviceModel = "Desktop";
parameters.ApplicationVersion = "1.0.0";
_client.Send(new TdApi.SetTdlibParameters(parameters), null);
_client.Send(new TdApi.CheckDatabaseEncryptionKey(), null);
var request = new TdApi.SetTdlibParameters();
request.DatabaseDirectory = Windows.Storage.ApplicationData.Current.LocalFolder.Path;
request.UseSecretChats = true;
request.UseMessageDatabase = true;
request.ApiId = 94575;
request.ApiHash = "a3406de8d171bb422bb6ddf3bbd800e2";
request.SystemLanguageCode = "en";
request.DeviceModel = "Desktop";
request.ApplicationVersion = "1.0.0";
_client.Send(request, null);
}
public void Print(String str)
@ -97,6 +96,18 @@ namespace TdApp
AcceptCommand(command);
_client.Send(new TdApi.SetAuthenticationPhoneNumber(args[1], null), _handler);
}
else if (command.StartsWith("sae"))
{
var args = command.Split(" ".ToCharArray(), 2);
AcceptCommand(command);
_client.Send(new TdApi.SetAuthenticationEmailAddress(args[1]), _handler);
}
else if (command.StartsWith("caec"))
{
var args = command.Split(" ".ToCharArray(), 2);
AcceptCommand(command);
_client.Send(new TdApi.CheckAuthenticationEmailCode(new TdApi.EmailAddressAuthenticationCode(args[1])), _handler);
}
else if (command.StartsWith("cac"))
{
var args = command.Split(" ".ToCharArray(), 2);

View File

@ -1,6 +1,6 @@
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011">
<Metadata>
<Identity Id="Telegram.Td.UWP" Version="1.8.5" Language="en-US" Publisher="Telegram LLC" />
<Identity Id="Telegram.Td.UWP" Version="1.8.6" 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

@ -734,14 +734,14 @@ class TdClient {
prepareQuery(query) {
if (query['@type'] === 'setTdlibParameters') {
query.parameters.database_directory = this.tdfs.dbFileSystem.root;
query.parameters.files_directory = this.tdfs.inboundFileSystem.root;
query.database_directory = this.tdfs.dbFileSystem.root;
query.files_directory = this.tdfs.inboundFileSystem.root;
const useDb = this.useDatabase;
query.parameters.use_file_database = useDb;
query.parameters.use_chat_info_database = useDb;
query.parameters.use_message_database = useDb;
query.parameters.use_secret_chats = useDb;
query.use_file_database = useDb;
query.use_chat_info_database = useDb;
query.use_message_database = useDb;
query.use_secret_chats = useDb;
}
if (query['@type'] === 'getLanguagePackString') {
query.language_pack_database_path =

View File

@ -141,18 +141,29 @@ EOT
* This class is a base class for all TDLib interface classes.
*/
EOT
);
$this->addDocumentation(" public Object() {", <<<EOT
/**
* Default Object constructor.
*/
EOT
);
$this->addDocumentation(' public abstract int getConstructor();', <<<EOT
/**
* @return identifier uniquely determining type of the object.
* Returns an identifier uniquely determining type of the object.
*
* @return a unique identifier of the object type.
*/
EOT
);
$this->addDocumentation(' public native String toString();', <<<EOT
/**
* @return string representation of the object.
* Returns a string representation of the object.
*
* @return a string representation of the object.
*/
EOT
);
@ -162,6 +173,13 @@ EOT
* This class is a base class for all TDLib interface function-classes.
*/
EOT
);
$this->addDocumentation(" public Function() {", <<<EOT
/**
* Default Function constructor.
*/
EOT
);
$this->addDocumentation(' public static final int CONSTRUCTOR', <<<EOT
@ -187,6 +205,12 @@ EOT
* $documentation
*/
EOT
);
$this->addDocumentation(" public $class_name() {", <<<EOT
/**
* Default class constructor.
*/
EOT
);
}

View File

@ -22,25 +22,6 @@ error code:int32 message:string = Error;
ok = Ok;
//@description Contains parameters for TDLib initialization
//@use_test_dc If set to true, the Telegram test environment will be used instead of the production environment
//@database_directory The path to the directory for the persistent database; if empty, the current working directory will be used
//@files_directory The path to the directory for storing files; if empty, database_directory will be used
//@use_file_database If set to true, information about downloaded and uploaded files will be saved between application restarts
//@use_chat_info_database If set to true, the library will maintain a cache of users, basic groups, supergroups, channels and secret chats. Implies use_file_database
//@use_message_database If set to true, the library will maintain a cache of chats and messages. Implies use_chat_info_database
//@use_secret_chats If set to true, support for secret chats will be enabled
//@api_id Application identifier for Telegram API access, which can be obtained at https://my.telegram.org
//@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. 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
tdlibParameters use_test_dc:Bool database_directory:string files_directory:string use_file_database:Bool use_chat_info_database:Bool use_message_database:Bool use_secret_chats:Bool api_id:int32 api_hash:string system_language_code:string device_model:string system_version:string application_version:string enable_storage_optimizer:Bool ignore_file_names:Bool = TdlibParameters;
//@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 from another active session @length Length of the code
@ -66,6 +47,18 @@ authenticationCodeInfo phone_number:string type:AuthenticationCodeType next_type
emailAddressAuthenticationCodeInfo email_address_pattern:string length:int32 = EmailAddressAuthenticationCodeInfo;
//@class EmailAddressAuthentication @description Contains authentication data for a email address
//@description An authentication code delivered to a user's email address @code The code
emailAddressAuthenticationCode code:string = EmailAddressAuthentication;
//@description An authentication token received through Apple ID @token The token
emailAddressAuthenticationAppleId token:string = EmailAddressAuthentication;
//@description An authentication token received through Google ID @token The token
emailAddressAuthenticationGoogleId token:string = EmailAddressAuthentication;
//@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;
@ -83,15 +76,22 @@ termsOfService text:formattedText min_user_age:int32 show_popup:Bool = TermsOfSe
//@class AuthorizationState @description Represents the current authorization state of the TDLib client
//@description TDLib needs TdlibParameters for initialization
//@description Initializetion parameters are needed. Call `setTdlibParameters` to provide them
authorizationStateWaitTdlibParameters = AuthorizationState;
//@description TDLib needs an encryption key to decrypt the local database @is_encrypted True, if the database is currently encrypted
authorizationStateWaitEncryptionKey is_encrypted:Bool = AuthorizationState;
//@description TDLib needs the user's phone number to authorize. Call `setAuthenticationPhoneNumber` to provide the phone number, or use `requestQrCodeAuthentication`, or `checkAuthenticationBotToken` for other authentication options
authorizationStateWaitPhoneNumber = AuthorizationState;
//@description TDLib needs the user's email address to authorize. Call `setAuthenticationEmailAddress` to provide the email address, or directly call `checkAuthenticationEmailCode` with Apple ID/Google ID token if allowed
//@allow_apple_id True, if authorization through Apple ID is allowed @allow_google_id True, if authorization through Google ID is allowed
authorizationStateWaitEmailAddress allow_apple_id:Bool allow_google_id:Bool = AuthorizationState;
//@description TDLib needs the user's authentication code sent to an email address to authorize. Call `checkAuthenticationEmailCode` to provide the code
//@allow_apple_id True, if authorization through Apple ID is allowed @allow_google_id True, if authorization through Google ID is allowed
//@code_info Information about the sent authentication code
//@next_phone_number_authorization_date Point in time (Unix timestamp) when the user will be able to authorize with a code sent to the user's phone number; 0 if unknown
authorizationStateWaitEmailCode allow_apple_id:Bool allow_google_id:Bool code_info:emailAddressAuthenticationCodeInfo next_phone_number_authorization_date:int32 = AuthorizationState;
//@description TDLib needs the user's authentication code to authorize @code_info Information about the authorization code that was sent
authorizationStateWaitCode code_info:authenticationCodeInfo = AuthorizationState;
@ -122,8 +122,9 @@ authorizationStateClosed = AuthorizationState;
//@description Represents the current state of 2-step verification @has_password True, if a 2-step verification password is set @password_hint Hint for the password; may be empty
//@has_recovery_email_address True, if a recovery email is set @has_passport_data True, if some Telegram Passport elements were saved
//@recovery_email_address_code_info Information about the recovery email address to which the confirmation email was sent; may be null
//@login_email_address_pattern Pattern of the email address set up for logging in
//@pending_reset_date If not 0, point in time (Unix timestamp) after which the 2-step verification password can be reset immediately using resetPassword
passwordState has_password:Bool password_hint:string has_recovery_email_address:Bool has_passport_data:Bool recovery_email_address_code_info:emailAddressAuthenticationCodeInfo pending_reset_date:int32 = PasswordState;
passwordState has_password:Bool password_hint:string has_recovery_email_address:Bool has_passport_data:Bool recovery_email_address_code_info:emailAddressAuthenticationCodeInfo login_email_address_pattern:string pending_reset_date:int32 = PasswordState;
//@description Contains information about the current recovery email address @recovery_email_address Recovery email address
recoveryEmailAddress recovery_email_address:string = RecoveryEmailAddress;
@ -162,6 +163,9 @@ remoteFile id:string unique_id:string is_uploading_active:Bool is_uploading_comp
//@remote Information about the remote copy of the file
file id:int32 size:int53 expected_size:int53 local:localFile remote:remoteFile = File;
//@description Represents a list of files @files List of files
files files:vector<file> = Files;
//@class InputFile @description Points to a file
@ -480,14 +484,20 @@ chatPermissions can_send_messages:Bool can_send_media_messages:Bool can_send_pol
chatAdministratorRights can_manage_chat:Bool can_change_info:Bool can_post_messages:Bool can_edit_messages:Bool can_delete_messages:Bool can_invite_users:Bool can_restrict_members:Bool can_pin_messages:Bool can_promote_members:Bool can_manage_video_chats:Bool is_anonymous:Bool = ChatAdministratorRights;
//@description Describes an option for gifting Telegram Premium to a user
//@description Describes an option for buying Telegram Premium to a user
//@currency ISO 4217 currency code for Telegram Premium subscription payment
//@amount The amount to pay, in the smallest units of the currency
//@discount_percentage The discount associated with this gift option, as a percentage
//@discount_percentage The discount associated with this option, as a percentage
//@month_count Number of month the Telegram Premium subscription will be active
//@store_product_id Identifier of the store product associated with the option
//@payment_link An internal link to be opened for gifting Telegram Premium to the user if store payment isn't possible; may be null if direct payment isn't available
premiumGiftOption currency:string amount:int53 discount_percentage:int32 month_count:int32 store_product_id:string payment_link:InternalLinkType = PremiumGiftOption;
//@payment_link An internal link to be opened for buying Telegram Premium to the user if store payment isn't possible; may be null if direct payment isn't available
premiumPaymentOption currency:string amount:int53 discount_percentage:int32 month_count:int32 store_product_id:string payment_link:InternalLinkType = PremiumPaymentOption;
//@description Describes a custom emoji to be shown instead of the Telegram Premium badge @custom_emoji_id Identifier of the custom emoji in stickerFormatTgs format. If the custom emoji belongs to the sticker set GetOption("themed_emoji_statuses_sticker_set_id"), then it's color must be changed to the color of the Telegram Premium badge
emojiStatus custom_emoji_id:int64 = EmojiStatus;
//@description Contains a list of emoji statuses @emoji_statuses The list of emoji statuses
emojiStatuses emoji_statuses:vector<emojiStatus> = EmojiStatuses;
//@description Represents a user
@ -498,6 +508,7 @@ premiumGiftOption currency:string amount:int53 discount_percentage:int32 month_c
//@phone_number Phone number of the user
//@status Current online status of the user
//@profile_photo Profile photo of the user; may be null
//@emoji_status Emoji status to be shown instead of the default Telegram Premium badge; may be null. For Telegram Premium users only
//@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
@ -510,7 +521,7 @@ premiumGiftOption currency:string amount:int53 discount_percentage:int32 month_c
//@type Type of the user
//@language_code IETF language tag of the user's language; only available to bots
//@added_to_attachment_menu True, if the user added the current bot to attachment menu; only available to bots
user id:int53 first_name:string last_name:string username:string phone_number:string status:UserStatus profile_photo:profilePhoto is_contact:Bool is_mutual_contact:Bool is_verified:Bool is_premium:Bool is_support:Bool restriction_reason:string is_scam:Bool is_fake:Bool have_access:Bool type:UserType language_code:string added_to_attachment_menu:Bool = User;
user id:int53 first_name:string last_name:string username:string phone_number:string status:UserStatus profile_photo:profilePhoto emoji_status:emojiStatus is_contact:Bool is_mutual_contact:Bool is_verified:Bool is_premium:Bool is_support:Bool restriction_reason:string is_scam:Bool is_fake:Bool have_access:Bool type:UserType language_code:string added_to_attachment_menu:Bool = User;
//@description Contains information about a bot
@ -537,7 +548,7 @@ botInfo share_text:string description:string photo:photo animation:animation men
//@premium_gift_options The list of available options for gifting Telegram Premium to the user
//@group_in_common_count Number of group chats where both the other user and the current user are a member; 0 for the current user
//@bot_info For bots, information about the bot; may be null
userFullInfo photo:chatPhoto is_blocked:Bool can_be_called:Bool supports_video_calls:Bool has_private_calls:Bool has_private_forwards:Bool has_restricted_voice_and_video_note_messages:Bool need_phone_number_privacy_exception:Bool bio:formattedText premium_gift_options:vector<premiumGiftOption> group_in_common_count:int32 bot_info:botInfo = UserFullInfo;
userFullInfo photo:chatPhoto is_blocked:Bool can_be_called:Bool supports_video_calls:Bool has_private_calls:Bool has_private_forwards:Bool has_restricted_voice_and_video_note_messages:Bool need_phone_number_privacy_exception:Bool bio:formattedText premium_gift_options:vector<premiumPaymentOption> group_in_common_count:int32 bot_info:botInfo = UserFullInfo;
//@description Represents a list of users @total_count Approximate total number of users found @user_ids A list of user identifiers
users total_count:int32 user_ids:vector<int53> = Users;
@ -821,6 +832,15 @@ messageForwardOriginChannel chat_id:int53 message_id:int53 author_signature:stri
messageForwardOriginMessageImport sender_name:string = MessageForwardOrigin;
//@class ReactionType @description Describes type of message reaction
//@description A reaction with an emoji @emoji Text representation of the reaction
reactionTypeEmoji emoji:string = ReactionType;
//@description A reaction with a custom emoji @custom_emoji_id Unique identifier of the custom emoji
reactionTypeCustomEmoji custom_emoji_id:int64 = ReactionType;
//@description Contains information about a forwarded message
//@origin Origin of a forwarded message
//@date Point in time (Unix timestamp) when the message was originally sent
@ -838,11 +858,11 @@ messageForwardInfo origin:MessageForwardOrigin date:int32 public_service_announc
messageReplyInfo reply_count:int32 recent_replier_ids:vector<MessageSender> last_read_inbox_message_id:int53 last_read_outbox_message_id:int53 last_message_id:int53 = MessageReplyInfo;
//@description Contains information about a reaction to a message
//@reaction Text representation of the reaction
//@type Type of the reaction
//@total_count Number of times the reaction was added
//@is_chosen True, if the reaction is chosen by the current user
//@recent_sender_ids Identifiers of at most 3 recent message senders, added the reaction; available in private, basic group and supergroup chats
messageReaction reaction:string total_count:int32 is_chosen:Bool recent_sender_ids:vector<MessageSender> = MessageReaction;
messageReaction type:ReactionType total_count:int32 is_chosen:Bool recent_sender_ids:vector<MessageSender> = MessageReaction;
//@description Contains information about interactions with a message
//@view_count Number of times the message was viewed
@ -852,10 +872,10 @@ messageReaction reaction:string total_count:int32 is_chosen:Bool recent_sender_i
messageInteractionInfo view_count:int32 forward_count:int32 reply_info:messageReplyInfo reactions:vector<messageReaction> = MessageInteractionInfo;
//@description Contains information about an unread reaction to a message
//@reaction Text representation of the reaction
//@type Type of the reaction
//@sender_id Identifier of the sender, added the reaction
//@is_big True, if the reaction was added with a big animation
unreadReaction reaction:string sender_id:MessageSender is_big:Bool = UnreadReaction;
unreadReaction type:ReactionType sender_id:MessageSender is_big:Bool = UnreadReaction;
//@class MessageSendingState @description Contains information about the sending state of the message
@ -888,6 +908,7 @@ messageSendingStateFailed error_code:int32 error_message:string can_retry:Bool n
//@can_get_message_thread True, if information about the message thread is available through getMessageThread and getMessageThreadHistory
//@can_get_viewers True, if chat members already viewed the message can be received through getMessageViewers
//@can_get_media_timestamp_links True, if media timestamp links can be generated for media timestamp entities in the message text, caption or web page description through getMessageLink
//@can_report_reactions True, if reactions on the message can be reported through reportMessageReactions
//@has_timestamped_media True, if media timestamp entities refers to a media in this message as opposed to a media in the replied message
//@is_channel_post True, if the message is a channel post. All messages to channels are channel posts, all other messages are not channel posts
//@contains_unread_mention True, if the message contains an unread mention for the current user
@ -907,7 +928,7 @@ messageSendingStateFailed error_code:int32 error_message:string can_retry:Bool n
//@restriction_reason If non-empty, contains a human-readable description of the reason why access to this message must be restricted
//@content Content of the message
//@reply_markup Reply markup for the message; may be null
message id:int53 sender_id:MessageSender chat_id:int53 sending_state:MessageSendingState scheduling_state:MessageSchedulingState is_outgoing:Bool is_pinned:Bool can_be_edited:Bool can_be_forwarded:Bool can_be_saved:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_get_added_reactions:Bool can_get_statistics:Bool can_get_message_thread:Bool can_get_viewers:Bool can_get_media_timestamp_links:Bool has_timestamped_media:Bool is_channel_post:Bool contains_unread_mention:Bool date:int32 edit_date:int32 forward_info:messageForwardInfo interaction_info:messageInteractionInfo unread_reactions:vector<unreadReaction> reply_in_chat_id:int53 reply_to_message_id:int53 message_thread_id:int53 ttl:int32 ttl_expires_in:double via_bot_user_id:int53 author_signature:string media_album_id:int64 restriction_reason:string content:MessageContent reply_markup:ReplyMarkup = Message;
message id:int53 sender_id:MessageSender chat_id:int53 sending_state:MessageSendingState scheduling_state:MessageSchedulingState is_outgoing:Bool is_pinned:Bool can_be_edited:Bool can_be_forwarded:Bool can_be_saved:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_get_added_reactions:Bool can_get_statistics:Bool can_get_message_thread:Bool can_get_viewers:Bool can_get_media_timestamp_links:Bool can_report_reactions:Bool has_timestamped_media:Bool is_channel_post:Bool contains_unread_mention:Bool date:int32 edit_date:int32 forward_info:messageForwardInfo interaction_info:messageInteractionInfo unread_reactions:vector<unreadReaction> reply_in_chat_id:int53 reply_to_message_id:int53 message_thread_id:int53 ttl:int32 ttl_expires_in:double via_bot_user_id:int53 author_signature:string media_album_id:int64 restriction_reason:string content:MessageContent reply_markup:ReplyMarkup = Message;
//@description Contains a list of messages @total_count Approximate total number of messages found @messages List of messages; messages may be null
messages total_count:int32 messages:vector<message> = Messages;
@ -1072,6 +1093,15 @@ chatSourcePublicServiceAnnouncement type:string text:string = ChatSource;
chatPosition list:ChatList order:int64 is_pinned:Bool source:ChatSource = ChatPosition;
//@class ChatAvailableReactions @description Describes reactions available in the chat
//@description All reactions are available in the chat
chatAvailableReactionsAll = ChatAvailableReactions;
//@description Only specific reactions are available in the chat @reactions The list of reactions
chatAvailableReactionsSome reactions:vector<ReactionType> = ChatAvailableReactions;
//@description Describes a video chat
//@group_call_id Group call identifier of an active video chat; 0 if none. Full information about the video chat can be received through the method getGroupCall
//@has_participants True, if the video chat has participants
@ -1102,7 +1132,7 @@ videoChat group_call_id:int32 has_participants:Bool default_participant_id:Messa
//@unread_mention_count Number of unread messages with a mention/reply in the chat
//@unread_reaction_count Number of messages with unread reactions in the chat
//@notification_settings Notification settings for the chat
//@available_reactions List of reactions, available in the chat
//@available_reactions Types of reaction, available in the chat
//@message_ttl Current message Time To Live setting (self-destruct timer) for the chat; 0 if not defined. TTL is counted from the time message or its content is viewed in secret chats and from the send date in other chats
//@theme_name If non-empty, name of a theme, set for the chat
//@action_bar Information about actions which must be possible to do through the chat action bar; may be null
@ -1111,7 +1141,7 @@ videoChat group_call_id:int32 has_participants:Bool default_participant_id:Messa
//@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 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> message_sender_id:MessageSender has_protected_content:Bool is_marked_as_unread:Bool is_blocked: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 unread_reaction_count:int32 notification_settings:chatNotificationSettings available_reactions:vector<string> message_ttl:int32 theme_name:string action_bar:ChatActionBar video_chat:videoChat pending_join_requests:chatJoinRequestsInfo reply_markup_message_id:int53 draft_message:draftMessage client_data:string = Chat;
chat id:int53 type:ChatType title:string photo:chatPhotoInfo permissions:chatPermissions last_message:message positions:vector<chatPosition> message_sender_id:MessageSender has_protected_content:Bool is_marked_as_unread:Bool is_blocked: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 unread_reaction_count:int32 notification_settings:chatNotificationSettings available_reactions:ChatAvailableReactions message_ttl:int32 theme_name:string action_bar:ChatActionBar video_chat:videoChat pending_join_requests:chatJoinRequestsInfo reply_markup_message_id:int53 draft_message:draftMessage client_data:string = Chat;
//@description Represents a list of chats @total_count Approximate total number of chats found @chat_ids List of chat identifiers
chats total_count:int32 chat_ids:vector<int53> = Chats;
@ -2135,8 +2165,9 @@ messageSchedulingStateSendWhenOnline = MessageSchedulingState;
//@disable_notification Pass true to disable notification for the message
//@from_background Pass true if the message is sent from the background
//@protect_content Pass true if the content of the message must be protected from forwarding and saving; for bots only
//@update_order_of_installed_sticker_sets Pass true if the user explicitly chosen a sticker or a custom emoji from an installed sticker set; applicable only to sendMessage and sendMessageAlbum
//@scheduling_state Message scheduling state; pass null to send message immediately. Messages sent to a secret chat, live location messages and self-destructing messages can't be scheduled
messageSendOptions disable_notification:Bool from_background:Bool protect_content:Bool scheduling_state:MessageSchedulingState = MessageSendOptions;
messageSendOptions disable_notification:Bool from_background:Bool protect_content:Bool update_order_of_installed_sticker_sets:Bool scheduling_state:MessageSchedulingState = MessageSendOptions;
//@description Options to be used when a message content is copied without reference to the original sender. Service messages and messageInvoice can't be copied
//@send_copy True, if content of the message needs to be copied without reference to the original sender. Always true if the message is forwarded to a secret chat or is local
@ -2560,24 +2591,26 @@ call id:int32 user_id:int53 is_outgoing:Bool is_video:Bool state:CallState = Cal
phoneNumberAuthenticationSettings allow_flash_call:Bool allow_missed_call:Bool is_current_phone_number:Bool allow_sms_retriever_api:Bool authentication_tokens:vector<string> = PhoneNumberAuthenticationSettings;
//@description Represents a reaction applied to a message @reaction Text representation of the reaction @sender_id Identifier of the chat member, applied the reaction
addedReaction reaction:string sender_id:MessageSender = AddedReaction;
//@description Represents a reaction applied to a message @type Type of the reaction @sender_id Identifier of the chat member, applied the reaction
addedReaction type:ReactionType sender_id:MessageSender = AddedReaction;
//@description Represents a list of reactions added to a message @total_count The total number of found reactions @reactions The list of added reactions @next_offset The offset for the next request. If empty, there are no more results
addedReactions total_count:int32 reactions:vector<addedReaction> next_offset:string = AddedReactions;
//@description Represents an available reaction @reaction Text representation of the reaction @needs_premium True, if Telegram Premium is needed to send the reaction
availableReaction reaction:string needs_premium:Bool = AvailableReaction;
//@description Represents an available reaction @type Type of the reaction @needs_premium True, if Telegram Premium is needed to send the reaction
availableReaction type:ReactionType needs_premium:Bool = AvailableReaction;
//@description Represents a list of available reactions @reactions List of reactions
availableReactions reactions:vector<availableReaction> = AvailableReactions;
//@description Represents a list of reactions that can be added to a message
//@top_reactions List of reactions to be shown at the top
//@recent_reactions List of recently used reactions
//@popular_reactions List of popular reactions
//@allow_custom_emoji True, if custom emoji reactions could be added by Telegram Premium subscribers
availableReactions top_reactions:vector<availableReaction> recent_reactions:vector<availableReaction> popular_reactions:vector<availableReaction> allow_custom_emoji:Bool = AvailableReactions;
//@description Contains stickers which must be used for reaction animation rendering
//@reaction Text representation of the reaction
//@description Contains information about a emoji reaction
//@emoji Text representation of the reaction
//@title Reaction title
//@is_active True, if the reaction can be added to new messages and enabled in chats
//@is_premium True, if the reaction is available only for Premium users
//@static_icon Static icon for the reaction
//@appear_animation Appear animation for the reaction
//@select_animation Select animation for the reaction
@ -2585,7 +2618,7 @@ availableReactions reactions:vector<availableReaction> = AvailableReactions;
//@effect_animation Effect animation for the reaction
//@around_animation Around animation for the reaction; may be null
//@center_animation Center animation for the reaction; may be null
reaction reaction:string title:string is_active:Bool is_premium:Bool static_icon:sticker appear_animation:sticker select_animation:sticker activate_animation:sticker effect_animation:sticker around_animation:sticker center_animation:sticker = Reaction;
emojiReaction emoji:string title:string is_active:Bool static_icon:sticker appear_animation:sticker select_animation:sticker activate_animation:sticker effect_animation:sticker around_animation:sticker center_animation:sticker = EmojiReaction;
//@description Represents a list of animations @animations List of animations
@ -2839,7 +2872,7 @@ chatEventMemberPromoted user_id:int53 old_status:ChatMemberStatus new_status:Cha
chatEventMemberRestricted member_id:MessageSender old_status:ChatMemberStatus new_status:ChatMemberStatus = ChatEventAction;
//@description The chat available reactions were changed @old_available_reactions Previous chat available reactions @new_available_reactions New chat available reactions
chatEventAvailableReactionsChanged old_available_reactions:vector<string> new_available_reactions:vector<string> = ChatEventAction;
chatEventAvailableReactionsChanged old_available_reactions:ChatAvailableReactions new_available_reactions:ChatAvailableReactions = ChatEventAction;
//@description The chat description was changed @old_description Previous chat description @new_description New chat description
chatEventDescriptionChanged old_description:string new_description:string = ChatEventAction;
@ -3028,6 +3061,9 @@ premiumFeatureAdvancedChatManagement = PremiumFeature;
//@description A badge in the user's profile
premiumFeatureProfileBadge = PremiumFeature;
//@description A emoji status shown along with the user's name
premiumFeatureEmojiStatus = PremiumFeature;
//@description Profile photo animation on message and chat screens
premiumFeatureAnimatedProfilePhoto = PremiumFeature;
@ -3063,10 +3099,9 @@ premiumFeaturePromotionAnimation feature:PremiumFeature animation:animation = Pr
//@description Contains state of Telegram Premium subscription and promotion videos for Premium features
//@state Text description of the state of the current Premium subscription; may be empty if the current user has no Telegram Premium subscription
//@currency ISO 4217 currency code for Telegram Premium subscription payment
//@monthly_amount Monthly subscription payment for Telegram Premium subscription, in the smallest units of the currency
//@payment_options The list of available options for buying Telegram Premium
//@animations The list of available promotion animations for Premium features
premiumState state:formattedText currency:string monthly_amount:int53 animations:vector<premiumFeaturePromotionAnimation> = PremiumState;
premiumState state:formattedText payment_options:vector<premiumPaymentOption> animations:vector<premiumFeaturePromotionAnimation> = PremiumState;
//@class StorePaymentPurpose @description Describes a purpose of an in-store payment
@ -3704,6 +3739,9 @@ internalLinkTypeFilterSettings = InternalLinkType;
//@bot_username Username of the bot that owns the game @game_short_name Short name of the game
internalLinkTypeGame bot_username:string game_short_name:string = InternalLinkType;
//@description The link must be opened in an Instant View. Call getWebPageInstantView with the given URL to process the link @url URL to be passed to getWebPageInstantView
internalLinkTypeInstantView url:string = InternalLinkType;
//@description The link is a link to an invoice. Call getPaymentForm with the given invoice name to process the link @invoice_name Name of the invoice
internalLinkTypeInvoice invoice_name:string = InternalLinkType;
@ -4259,8 +4297,8 @@ updateChatReadOutbox chat_id:int53 last_read_outbox_message_id:int53 = Update;
//@description The chat action bar was changed @chat_id Chat identifier @action_bar The new value of the action bar; may be null
updateChatActionBar chat_id:int53 action_bar:ChatActionBar = Update;
//@description The chat available reactions were changed @chat_id Chat identifier @available_reactions The new list of reactions, available in the chat
updateChatAvailableReactions chat_id:int53 available_reactions:vector<string> = Update;
//@description The chat available reactions were changed @chat_id Chat identifier @available_reactions The new reactions, available in the chat
updateChatAvailableReactions chat_id:int53 available_reactions:ChatAvailableReactions = Update;
//@description A chat draft has changed. Be aware that the update may come in the currently opened chat but with old content of the draft. If the user has changed the content of the draft, this update mustn't be applied @chat_id Chat identifier @draft_message The new draft message; may be null @positions The new chat positions in the chat lists
updateChatDraftMessage chat_id:int53 draft_message:draftMessage positions:vector<chatPosition> = Update;
@ -4484,8 +4522,11 @@ updateAttachmentMenuBots bots:vector<attachmentMenuBot> = Update;
//@description A message was sent by an opened Web App, so the Web App needs to be closed @web_app_launch_id Identifier of Web App launch
updateWebAppMessageSent web_app_launch_id:int64 = Update;
//@description The list of supported reactions has changed @reactions The new list of supported reactions
updateReactions reactions:vector<reaction> = Update;
//@description The list of active emoji reactions has changed @emojis The new list of active emoji reactions
updateActiveEmojiReactions emojis:vector<string> = Update;
//@description The type of default reaction has changed @reaction_type The new type of the default reaction
updateDefaultReactionType reaction_type:ReactionType = Update;
//@description The list of supported dice emojis has changed @emojis The new list of supported dice emojis
updateDiceEmojis emojis:vector<string> = Update;
@ -4596,20 +4637,39 @@ testVectorStringObject value:vector<testString> = TestVectorStringObject;
getAuthorizationState = AuthorizationState;
//@description Sets the parameters for TDLib initialization. Works only when the current authorization state is authorizationStateWaitTdlibParameters @parameters Parameters for TDLib initialization
setTdlibParameters parameters:tdlibParameters = Ok;
//@description Checks the database encryption key for correctness. Works only when the current authorization state is authorizationStateWaitEncryptionKey @encryption_key Encryption key to check or set up
checkDatabaseEncryptionKey encryption_key:bytes = Ok;
//@description Sets the parameters for TDLib initialization. Works only when the current authorization state is authorizationStateWaitTdlibParameters
//@use_test_dc Pass true to use Telegram test environment instead of the production environment
//@database_directory The path to the directory for the persistent database; if empty, the current working directory will be used
//@files_directory The path to the directory for storing files; if empty, database_directory will be used
//@database_encryption_key Encryption key for the database
//@use_file_database Pass true to keep information about downloaded and uploaded files between application restarts
//@use_chat_info_database Pass true to keep cache of users, basic groups, supergroups, channels and secret chats between restarts. Implies use_file_database
//@use_message_database Pass true to keep cache of chats and messages between restarts. Implies use_chat_info_database
//@use_secret_chats Pass true to enable support for secret chats
//@api_id Application identifier for Telegram API access, which can be obtained at https://my.telegram.org
//@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. If empty, the version is automatically detected by TDLib
//@application_version Application version; must be non-empty
//@enable_storage_optimizer Pass true to automatically delete old files in background
//@ignore_file_names Pass true to ignore original file names for downloaded files. Otherwise, downloaded files are saved under names as close as possible to the original name
setTdlibParameters use_test_dc:Bool database_directory:string files_directory:string database_encryption_key:bytes use_file_database:Bool use_chat_info_database:Bool use_message_database:Bool use_secret_chats:Bool api_id:int32 api_hash:string system_language_code:string device_model:string system_version:string application_version:string enable_storage_optimizer:Bool ignore_file_names:Bool = Ok;
//@description Sets the phone number of the user and sends an authentication code to the user. 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
//@phone_number The phone number of the user, in international format @settings Settings for the authentication of the user's phone number; pass null to use default settings
setAuthenticationPhoneNumber phone_number:string settings:phoneNumberAuthenticationSettings = Ok;
//@description Re-sends an authentication code to the user. Works only when the current authorization state is authorizationStateWaitCode, the next_code_type of the result is not null and the server-specified timeout has passed
//@description Sets the email address of the user and sends an authentication code to the email address. Works only when the current authorization state is authorizationStateWaitEmailAddress @email_address The email address of the user
setAuthenticationEmailAddress email_address:string = Ok;
//@description Resends an authentication code to the user. Works only when the current authorization state is authorizationStateWaitCode, the next_code_type of the result is not null and the server-specified timeout has passed, or when the current authorization state is authorizationStateWaitEmailCode
resendAuthenticationCode = Ok;
//@description Checks the authentication of a email address. Works only when the current authorization state is authorizationStateWaitEmailCode @code Email address authentication to check
checkAuthenticationEmailCode code:EmailAddressAuthentication = Ok;
//@description Checks the authentication code. Works only when the current authorization state is authorizationStateWaitCode @code Authentication code to check
checkAuthenticationCode code:string = Ok;
@ -4667,6 +4727,15 @@ getPasswordState = PasswordState;
//@old_password Previous 2-step verification password of the user @new_password New 2-step verification password of the user; may be empty to remove the password @new_hint New password hint; may be empty @set_recovery_email_address Pass true to change also the recovery email address @new_recovery_email_address New recovery email address; may be empty
setPassword old_password:string new_password:string new_hint:string set_recovery_email_address:Bool new_recovery_email_address:string = PasswordState;
//@description Changes the login email address of the user. The change will not be applied until the new login email address is confirmed with `checkLoginEmailAddressCode`. To use Apple ID/Google ID instead of a email address, call `checkLoginEmailAddressCode` directly @new_login_email_address New login email address
setLoginEmailAddress new_login_email_address:string = EmailAddressAuthenticationCodeInfo;
//@description Resends the login email address verification code
resendLoginEmailAddressCode = EmailAddressAuthenticationCodeInfo;
//@description Checks the login email address authentication @code Email address authentication to check
checkLoginEmailAddressCode code:EmailAddressAuthentication = Ok;
//@description Returns a 2-step verification recovery email address that was previously set up. This method can be used to verify a password provided by the user @password The 2-step verification password for the current user
getRecoveryEmailAddress password:string = RecoveryEmailAddress;
@ -5128,25 +5197,45 @@ editInlineMessageReplyMarkup inline_message_id:string reply_markup:ReplyMarkup =
editMessageSchedulingState chat_id:int53 message_id:int53 scheduling_state:MessageSchedulingState = Ok;
//@description Returns reactions, which can be added to a message. The list can change after updateReactions, updateChatAvailableReactions for the chat, or updateMessageInteractionInfo for the message
//@chat_id Identifier of the chat to which the message belongs
//@message_id Identifier of the message
getMessageAvailableReactions chat_id:int53 message_id:int53 = AvailableReactions;
//@description Returns information about a emoji reaction. Returns a 404 error if the reaction is not found @emoji Text representation of the reaction
getEmojiReaction emoji:string = EmojiReaction;
//@description Changes chosen reaction for a message
//@description Returns TGS files with generic animations for custom emoji reactions
getCustomEmojiReactionAnimations = Files;
//@description Returns reactions, which can be added to a message. The list can change after updateActiveEmojiReactions, updateChatAvailableReactions for the chat, or updateMessageInteractionInfo for the message
//@chat_id Identifier of the chat to which the message belongs
//@message_id Identifier of the message
//@reaction Text representation of the new chosen reaction. Can be an empty string or the currently chosen non-big reaction to remove the reaction
//@row_size Number of reaction per row, 5-25
getMessageAvailableReactions chat_id:int53 message_id:int53 row_size:int32 = AvailableReactions;
//@description Clears the list of recently used reactions
clearRecentReactions = Ok;
//@description Adds a reaction to a message. Use getMessageAvailableReactions to receive the list of available reactions for the message
//@chat_id Identifier of the chat to which the message belongs
//@message_id Identifier of the message
//@reaction_type Type of the reaction to add
//@is_big Pass true if the reaction is added with a big animation
setMessageReaction chat_id:int53 message_id:int53 reaction:string is_big:Bool = Ok;
//@update_recent_reactions Pass true if the reaction needs to be added to recent reactions
addMessageReaction chat_id:int53 message_id:int53 reaction_type:ReactionType is_big:Bool update_recent_reactions:Bool = Ok;
//@description Removes a reaction from a message. A chosen reaction can always be removed
//@chat_id Identifier of the chat to which the message belongs
//@message_id Identifier of the message
//@reaction_type Type of the reaction to remove
removeMessageReaction chat_id:int53 message_id:int53 reaction_type:ReactionType = Ok;
//@description Returns reactions added for a message, along with their sender
//@chat_id Identifier of the chat to which the message belongs
//@message_id Identifier of the message
//@reaction If non-empty, only added reactions with the specified text representation will be returned
//@reaction_type Type of the reactions to return; pass null to return all added reactions
//@offset Offset of the first entry to return as received from the previous request; use empty string to get the first chunk of results
//@limit The maximum number of reactions to be returned; must be positive and can't be greater than 100
getMessageAddedReactions chat_id:int53 message_id:int53 reaction:string offset:string limit:int32 = AddedReactions;
getMessageAddedReactions chat_id:int53 message_id:int53 reaction_type:ReactionType offset:string limit:int32 = AddedReactions;
//@description Changes type of default reaction for the current user @reaction_type New type of the default reaction
setDefaultReactionType reaction_type:ReactionType = Ok;
//@description Returns all entities (mentions, hashtags, cashtags, bot commands, bank card numbers, URLs, and email addresses) contained in the text. Can be called synchronously @text The text in which to look for entites
@ -5244,7 +5333,8 @@ answerInlineQuery inline_query_id:int64 is_personal:Bool results:vector<InputInl
//@bot_user_id Identifier of the target bot
//@url The URL from the keyboardButtonTypeWebApp button
//@theme Preferred Web App theme; pass null to use the default theme
getWebAppUrl bot_user_id:int53 url:string theme:themeParameters = HttpUrl;
//@application_name Short name of the application; 0-64 English letters, digits, and underscores
getWebAppUrl bot_user_id:int53 url:string theme:themeParameters application_name:string = HttpUrl;
//@description Sends data received from a keyboardButtonTypeWebApp Web App to a bot
//@bot_user_id Identifier of the target bot @button_text Text of the keyboardButtonTypeWebApp button, which opened the Web App @data Received data
@ -5256,8 +5346,9 @@ sendWebAppData bot_user_id:int53 button_text:string data:string = Ok;
//@bot_user_id Identifier of the bot, providing the Web App
//@url The URL from an inlineKeyboardButtonTypeWebApp button, a botMenuButton button, or an internalLinkTypeAttachmentMenuBot link, or an empty string otherwise
//@theme Preferred Web App theme; pass null to use the default theme
//@application_name Short name of the application; 0-64 English letters, digits, and underscores
//@reply_to_message_id Identifier of the replied message for the message sent by the Web App; 0 if none
openWebApp chat_id:int53 bot_user_id:int53 url:string theme:themeParameters reply_to_message_id:int53 = WebAppInfo;
openWebApp chat_id:int53 bot_user_id:int53 url:string theme:themeParameters application_name:string reply_to_message_id:int53 = WebAppInfo;
//@description Informs TDLib that a previously opened Web App was closed @web_app_launch_id Identifier of Web App launch, received from openWebApp
closeWebApp web_app_launch_id:int64 = Ok;
@ -5441,8 +5532,8 @@ 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 reactions, available in a chat. Available for basic groups, supergroups, and channels. Requires can_change_info administrator right @chat_id Identifier of the chat @available_reactions New list of reactions, available in the chat. All reactions must be active
setChatAvailableReactions chat_id:int53 available_reactions:vector<string> = Ok;
//@description Changes reactions, available in a chat. Available for basic groups, supergroups, and channels. Requires can_change_info administrator right @chat_id Identifier of the chat @available_reactions Reactions available in the chat. All emoji reactions must be active
setChatAvailableReactions chat_id:int53 available_reactions:ChatAvailableReactions = Ok;
//@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;
@ -5567,6 +5658,19 @@ getAttachmentMenuBot bot_user_id:int53 = AttachmentMenuBot;
toggleBotIsAddedToAttachmentMenu bot_user_id:int53 is_added:Bool = Ok;
//@description Returns up to 8 themed emoji statuses, which color must be changed to the color of the Telegram Premium badge
getThemedEmojiStatuses = EmojiStatuses;
//@description Returns recent emoji statuses
getRecentEmojiStatuses = EmojiStatuses;
//@description Returns default emoji statuses
getDefaultEmojiStatuses = EmojiStatuses;
//@description Clears the list of recently used emoji statuses
clearRecentEmojiStatuses = Ok;
//@description Downloads a file from the cloud. Download progress and completion of the download will be notified through updateFile updates
//@file_id Identifier of the file to download
//@priority Priority of the download (1-32). The higher the priority, the earlier the file will be downloaded. If the priorities of two files are equal, then the last one for which downloadFile/addFileToDownloads was called will be downloaded first
@ -6073,6 +6177,11 @@ setBio bio:string = Ok;
//@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 emoji status of the current user; for Telegram Premium users only
//@emoji_status New emoji status; pass null to switch to the default badge
//@duration Duration of the status, in seconds; pass 0 to keep the status active until it will be changed manually
setEmojiStatus emoji_status:emojiStatus duration:int32 = Ok;
//@description Changes the location of the current user. Needs to be called if GetOption("is_location_visible") is true and location changes for more than 1 kilometer @location The new location of the user
setLocation location:location = Ok;
@ -6080,7 +6189,7 @@ setLocation location:location = Ok;
//@phone_number The new phone number of the user in international format @settings Settings for the authentication of the user's phone number; pass null to use default settings
changePhoneNumber phone_number:string settings:phoneNumberAuthenticationSettings = AuthenticationCodeInfo;
//@description Re-sends the authentication code sent to confirm a new phone number for the current user. Works only if the previously received authenticationCodeInfo next_code_type was not null and the server-specified timeout has passed
//@description Resends the authentication code sent to confirm a new phone number for the current user. Works only if the previously received authenticationCodeInfo next_code_type was not null and the server-specified timeout has passed
resendChangePhoneNumberCode = AuthenticationCodeInfo;
//@description Checks the authentication code sent to confirm a new phone number of the user @code Authentication code to check
@ -6325,6 +6434,11 @@ reportChat chat_id:int53 message_ids:vector<int53> reason:ChatReportReason text:
//@chat_id Chat identifier @file_id Identifier of the photo to report. Only full photos from chatPhoto can be reported @reason The reason for reporting the chat photo @text Additional report details; 0-1024 characters
reportChatPhoto chat_id:int53 file_id:int32 reason:ChatReportReason text:string = Ok;
//@description Reports reactions set on a message to the Telegram moderators. Reactions on a message can be reported only if message.can_report_reactions
//@chat_id Chat identifier @message_id Message identifier @sender_id Identifier of the sender, which added the reaction
reportMessageReactions chat_id:int53 message_id:int53 sender_id:MessageSender = Ok;
//@description Returns detailed statistics about a chat. Currently, this method can be used only for supergroups and channels. Can be used only if supergroupFullInfo.can_get_statistics == true @chat_id Chat identifier @is_dark Pass true if a dark theme is used by the application
getChatStatistics chat_id:int53 is_dark:Bool = ChatStatistics;
@ -6409,7 +6523,7 @@ getPreferredCountryLanguage country_code:string = Text;
//@phone_number The phone number of the user, in international format @settings Settings for the authentication of the user's phone number; pass null to use default settings
sendPhoneNumberVerificationCode phone_number:string settings:phoneNumberAuthenticationSettings = AuthenticationCodeInfo;
//@description Re-sends the code to verify a phone number to be added to a user's Telegram Passport
//@description Resends the code to verify a phone number to be added to a user's Telegram Passport
resendPhoneNumberVerificationCode = AuthenticationCodeInfo;
//@description Checks the phone number verification code for Telegram Passport @code Verification code to check
@ -6419,7 +6533,7 @@ checkPhoneNumberVerificationCode code:string = Ok;
//@description Sends a code to verify an email address to be added to a user's Telegram Passport @email_address Email address
sendEmailAddressVerificationCode email_address:string = EmailAddressAuthenticationCodeInfo;
//@description Re-sends the code to verify an email address to be added to a user's Telegram Passport
//@description Resends the code to verify an email address to be added to a user's Telegram Passport
resendEmailAddressVerificationCode = EmailAddressAuthenticationCodeInfo;
//@description Checks the email address verification code for Telegram Passport @code Verification code to check

View File

@ -101,7 +101,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType;
storage.fileWebp#1081464c = storage.FileType;
userEmpty#d3bc4b7a id:long = User;
user#3ff6ecb0 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 apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true id:long 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;
user#5d99adee 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 apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true id:long 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 emoji_status:flags.30?EmojiStatus = User;
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
userProfilePhoto#82d1f706 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto;
@ -119,8 +119,8 @@ chatForbidden#6592a1a7 id:long title:string = Chat;
channel#8261ac61 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#d18ee226 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?Vector<string> = ChatFull;
channelFull#ea68a619 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?Vector<string> = ChatFull;
chatFull#c9d31138 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions = ChatFull;
channelFull#f2355507 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions = ChatFull;
chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant;
@ -315,7 +315,7 @@ updateChannelMessageViews#f226ac08 channel_id:long id:int views:int = Update;
updateChatParticipantAdmin#d7ca61a2 chat_id:long user_id:long is_admin:Bool version:int = Update;
updateNewStickerSet#688a30aa stickerset:messages.StickerSet = Update;
updateStickerSetsOrder#bb2d201 flags:# masks:flags.0?true emojis:flags.1?true order:Vector<long> = Update;
updateStickerSets#43ae3dec = Update;
updateStickerSets#31c24808 flags:# masks:flags.0?true emojis:flags.1?true = Update;
updateSavedGifs#9375341e = Update;
updateBotInlineQuery#496f379c flags:# query_id:long user_id:long query:string geo:flags.0?GeoPoint peer_type:flags.1?InlineQueryPeerType offset:string = Update;
updateBotInlineSend#12f12a07 flags:# user_id:long query:string geo:flags.0?GeoPoint id:string msg_id:flags.1?InputBotInlineMessageID = Update;
@ -384,6 +384,10 @@ updateBotMenuButton#14b85813 bot_id:long button:BotMenuButton = Update;
updateSavedRingtones#74d8be99 = Update;
updateTranscribedAudio#84cd5a flags:# pending:flags.0?true peer:Peer msg_id:int transcription_id:long text:string = Update;
updateReadFeaturedEmojiStickers#fb4c496c = Update;
updateUserEmojiStatus#28373599 user_id:long emoji_status:EmojiStatus = Update;
updateRecentEmojiStatuses#30f443db = Update;
updateRecentReactions#6f7863f4 = Update;
updateMoveStickerSetToTop#86fccf85 flags:# masks:flags.0?true emojis:flags.1?true stickerset:long = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -410,7 +414,7 @@ upload.fileCdnRedirect#f18cda44 dc_id:int file_token:bytes encryption_key:bytes
dcOption#18b7a10d flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true this_port_only:flags.5?true id:int ip_address:string port:int secret:flags.10?bytes = DcOption;
config#330b4067 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true pfs_enabled:flags.13?true force_try_ipv6:flags.14?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int pinned_infolder_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int = Config;
config#232566ac flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true pfs_enabled:flags.13?true force_try_ipv6:flags.14?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int pinned_infolder_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int reactions_default:flags.15?Reaction = Config;
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
@ -548,7 +552,7 @@ authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true pa
account.authorizations#4bff8ea0 authorization_ttl_days:int authorizations:Vector<Authorization> = account.Authorizations;
account.password#185b184f flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes pending_reset_date:flags.5?int = account.Password;
account.password#957b50fb flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes pending_reset_date:flags.5?int login_email_pattern:flags.6?string = account.Password;
account.passwordSettings#9a5c33e5 flags:# email:flags.0?string secure_settings:flags.1?SecureSecretSettings = account.PasswordSettings;
@ -572,6 +576,8 @@ inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet;
inputStickerSetDice#e67f520e emoticon:string = InputStickerSet;
inputStickerSetAnimatedEmojiAnimations#cde3739 = InputStickerSet;
inputStickerSetPremiumGifts#c88b3b02 = InputStickerSet;
inputStickerSetEmojiGenericAnimations#4c4d4ce = InputStickerSet;
inputStickerSetEmojiDefaultStatuses#29d0f5ee = InputStickerSet;
stickerSet#2dd14edc flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true videos:flags.6?true emojis:flags.7?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int thumb_document_id:flags.8?long count:int hash:int = StickerSet;
@ -707,6 +713,8 @@ auth.sentCodeTypeSms#c000bba2 length:int = auth.SentCodeType;
auth.sentCodeTypeCall#5353e5a7 length:int = auth.SentCodeType;
auth.sentCodeTypeFlashCall#ab03c6d9 pattern:string = auth.SentCodeType;
auth.sentCodeTypeMissedCall#82006484 prefix:string length:int = auth.SentCodeType;
auth.sentCodeTypeEmailCode#5a159841 flags:# apple_signin_allowed:flags.0?true google_signin_allowed:flags.1?true email_pattern:string length:int next_phone_login_date:flags.2?int = auth.SentCodeType;
auth.sentCodeTypeSetUpEmailRequired#a5491dea flags:# apple_signin_allowed:flags.0?true google_signin_allowed:flags.1?true = auth.SentCodeType;
messages.botCallbackAnswer#36585ea4 flags:# alert:flags.1?true has_url:flags.3?true native_ui:flags.4?true message:flags.0?string url:flags.2?string cache_time:int = messages.BotCallbackAnswer;
@ -933,7 +941,7 @@ channelAdminLogEventActionChangeHistoryTTL#6e941a38 prev_value:int new_value:int
channelAdminLogEventActionParticipantJoinByRequest#afb6144a invite:ExportedChatInvite approved_by:long = ChannelAdminLogEventAction;
channelAdminLogEventActionToggleNoForwards#cb2ac766 new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionSendMessage#278f2868 message:Message = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeAvailableReactions#9cf7f76a prev_value:Vector<string> new_value:Vector<string> = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeAvailableReactions#be4e0ef8 prev_value:ChatReactions new_value:ChatReactions = ChannelAdminLogEventAction;
channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
@ -1318,7 +1326,7 @@ messages.peerSettings#6880b94d settings:PeerSettings chats:Vector<Chat> users:Ve
auth.loggedOut#c3a2835f flags:# future_auth_token:flags.0?bytes = auth.LoggedOut;
reactionCount#6fb250d1 flags:# chosen:flags.0?true reaction:string count:int = ReactionCount;
reactionCount#a3d1cb80 flags:# chosen_order:flags.0?int reaction:Reaction count:int = ReactionCount;
messageReactions#4f2b9479 flags:# min:flags.0?true can_see_list:flags.2?true results:Vector<ReactionCount> recent_reactions:flags.1?Vector<MessagePeerReaction> = MessageReactions;
@ -1332,7 +1340,7 @@ messages.availableReactions#768e3aad hash:int reactions:Vector<AvailableReaction
messages.translateNoResult#67ca4737 = messages.TranslatedText;
messages.translateResultText#a214f7d0 text:string = messages.TranslatedText;
messagePeerReaction#51b67eff flags:# big:flags.0?true unread:flags.1?true peer_id:Peer reaction:string = MessagePeerReaction;
messagePeerReaction#b156fe9c flags:# big:flags.0?true unread:flags.1?true peer_id:Peer reaction:Reaction = MessagePeerReaction;
groupCallStreamChannel#80eb48af channel:int scale:int last_timestamp_ms:long = GroupCallStreamChannel;
@ -1385,7 +1393,7 @@ payments.exportedInvoice#aed0cbd9 url:string = payments.ExportedInvoice;
messages.transcribedAudio#93752c52 flags:# pending:flags.0?true transcription_id:long text:string = messages.TranscribedAudio;
help.premiumPromo#8a4f3c29 status_text:string status_entities:Vector<MessageEntity> video_sections:Vector<string> videos:Vector<Document> currency:string monthly_amount:long users:Vector<User> = help.PremiumPromo;
help.premiumPromo#5334759c status_text:string status_entities:Vector<MessageEntity> video_sections:Vector<string> videos:Vector<Document> period_options:Vector<PremiumSubscriptionOption> users:Vector<User> = help.PremiumPromo;
inputStorePaymentPremiumSubscription#a6751e66 flags:# restore:flags.0?true = InputStorePaymentPurpose;
inputStorePaymentGiftPremium#616f7fe8 user_id:InputUser currency:string amount:long = InputStorePaymentPurpose;
@ -1394,6 +1402,37 @@ premiumGiftOption#74c34319 flags:# months:int currency:string amount:long bot_ur
paymentFormMethod#88f8f21b url:string title:string = PaymentFormMethod;
emojiStatusEmpty#2de11aae = EmojiStatus;
emojiStatus#929b619d document_id:long = EmojiStatus;
emojiStatusUntil#fa30a8c7 document_id:long until:int = EmojiStatus;
account.emojiStatusesNotModified#d08ce645 = account.EmojiStatuses;
account.emojiStatuses#90c467d1 hash:long statuses:Vector<EmojiStatus> = account.EmojiStatuses;
reactionEmpty#79f5d419 = Reaction;
reactionEmoji#1b2286b8 emoticon:string = Reaction;
reactionCustomEmoji#8935fc73 document_id:long = Reaction;
chatReactionsNone#eafc32bc = ChatReactions;
chatReactionsAll#52928bca flags:# allow_custom:flags.0?true = ChatReactions;
chatReactionsSome#661d4037 reactions:Vector<Reaction> = ChatReactions;
messages.reactionsNotModified#b06fdbdf = messages.Reactions;
messages.reactions#eafdf716 hash:long reactions:Vector<Reaction> = messages.Reactions;
emailVerifyPurposeLoginSetup#4345be73 phone_number:string phone_code_hash:string = EmailVerifyPurpose;
emailVerifyPurposeLoginChange#527d22eb = EmailVerifyPurpose;
emailVerifyPurposePassport#bbf51685 = EmailVerifyPurpose;
emailVerificationCode#922e55a9 code:string = EmailVerification;
emailVerificationGoogle#db909ec2 token:string = EmailVerification;
emailVerificationApple#96d074fd token:string = EmailVerification;
account.emailVerified#2b96cd1b email:string = account.EmailVerified;
account.emailVerifiedLogin#e1bb0d61 email:string sent_code:auth.SentCode = account.EmailVerified;
premiumSubscriptionOption#b6f11ebe flags:# current:flags.1?true can_purchase_upgrade:flags.2?true months:int currency:string amount:long bot_url:string store_product:flags.0?string = PremiumSubscriptionOption;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1406,7 +1445,7 @@ invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X;
auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode;
auth.signUp#80eee427 phone_number:string phone_code_hash:string first_name:string last_name:string = auth.Authorization;
auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization;
auth.signIn#8d52a951 flags:# phone_number:string phone_code_hash:string phone_code:flags.0?string email_verification:flags.1?EmailVerification = auth.Authorization;
auth.logOut#3e72ba19 = auth.LoggedOut;
auth.resetAuthorizations#9fab0d1a = Bool;
auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization;
@ -1462,8 +1501,8 @@ account.getAuthorizationForm#a929597a bot_id:long scope:string public_key:string
account.acceptAuthorization#f3ed4c73 bot_id:long scope:string public_key:string value_hashes:Vector<SecureValueHash> credentials:SecureCredentialsEncrypted = Bool;
account.sendVerifyPhoneCode#a5a356f9 phone_number:string settings:CodeSettings = auth.SentCode;
account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_code:string = Bool;
account.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode;
account.verifyEmail#ecba39db email:string code:string = Bool;
account.sendVerifyEmailCode#98e037bb purpose:EmailVerifyPurpose email:string = account.SentEmailCode;
account.verifyEmail#32da4cf purpose:EmailVerifyPurpose verification:EmailVerification = account.EmailVerified;
account.initTakeoutSession#8ef3eab0 flags:# contacts:flags.0?true message_users:flags.1?true message_chats:flags.2?true message_megagroups:flags.3?true message_channels:flags.4?true files:flags.5?true file_max_size:flags.5?long = account.Takeout;
account.finishTakeoutSession#1d2652ee flags:# success:flags.0?true = Bool;
account.confirmPasswordEmail#8fdf1920 code:string = Bool;
@ -1500,6 +1539,10 @@ account.changeAuthorizationSettings#40f48462 flags:# hash:long encrypted_request
account.getSavedRingtones#e1902288 hash:long = account.SavedRingtones;
account.saveRingtone#3dea5b03 id:InputDocument unsave:Bool = account.SavedRingtone;
account.uploadRingtone#831a83a2 file:InputFile file_name:string mime_type:string = Document;
account.updateEmojiStatus#fbd3de6b emoji_status:EmojiStatus = Bool;
account.getDefaultEmojiStatuses#d6753386 hash:long = account.EmojiStatuses;
account.getRecentEmojiStatuses#f578105 hash:long = account.EmojiStatuses;
account.clearRecentEmojiStatuses#18201aae = Bool;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
@ -1536,8 +1579,8 @@ messages.deleteHistory#b08f922a flags:# just_clear:flags.0?true revoke:flags.1?t
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
messages.sendMessage#d9d75a4 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMedia#e25ff8e0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMessage#d9d75a4 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMedia#e25ff8e0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.forwardMessages#cc30290b flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.reportSpam#cf1592db peer:InputPeer = Bool;
messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings;
@ -1617,7 +1660,7 @@ messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool;
messages.getUnreadMentions#46578472 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readMentions#f0189d3 peer:InputPeer = messages.AffectedHistory;
messages.getRecentLocations#702a40e0 peer:InputPeer limit:int hash:long = messages.Messages;
messages.sendMultiMedia#f803138f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMultiMedia#f803138f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
messages.searchStickerSets#35705b8a flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets;
messages.getSplitRanges#1cff7e08 = Vector<MessageRange>;
@ -1676,12 +1719,12 @@ messages.hideChatJoinRequest#7fe7e815 flags:# approved:flags.0?true peer:InputPe
messages.hideAllChatJoinRequests#e085f4ea flags:# approved:flags.0?true peer:InputPeer link:flags.1?string = Updates;
messages.toggleNoForwards#b11eafa2 peer:InputPeer enabled:Bool = Updates;
messages.saveDefaultSendAs#ccfddf96 peer:InputPeer send_as:InputPeer = Bool;
messages.sendReaction#25690ce4 flags:# big:flags.1?true peer:InputPeer msg_id:int reaction:flags.0?string = Updates;
messages.sendReaction#d30d78d4 flags:# big:flags.1?true add_to_recent:flags.2?true peer:InputPeer msg_id:int reaction:flags.0?Vector<Reaction> = Updates;
messages.getMessagesReactions#8bba90e6 peer:InputPeer id:Vector<int> = Updates;
messages.getMessageReactionsList#e0ee6b77 flags:# peer:InputPeer id:int reaction:flags.0?string offset:flags.1?string limit:int = messages.MessageReactionsList;
messages.setChatAvailableReactions#14050ea6 peer:InputPeer available_reactions:Vector<string> = Updates;
messages.getMessageReactionsList#461b3f48 flags:# peer:InputPeer id:int reaction:flags.0?Reaction offset:flags.1?string limit:int = messages.MessageReactionsList;
messages.setChatAvailableReactions#feb16771 peer:InputPeer available_reactions:ChatReactions = Updates;
messages.getAvailableReactions#18dea0ac hash:int = messages.AvailableReactions;
messages.setDefaultReaction#d960c4d4 reaction:string = Bool;
messages.setDefaultReaction#4f47a016 reaction:Reaction = Bool;
messages.translateText#24ce6dee flags:# peer:flags.0?InputPeer msg_id:flags.0?int text:flags.1?string from_lang:flags.2?string to_lang:string = messages.TranslatedText;
messages.getUnreadReactions#e85bae1a peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readReactions#82e251d7 peer:InputPeer = messages.AffectedHistory;
@ -1689,9 +1732,9 @@ messages.searchSentMedia#107e31a0 q:string filter:MessagesFilter limit:int = mes
messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots;
messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot;
messages.toggleBotInAttachMenu#1aee33af bot:InputUser enabled:Bool = Bool;
messages.requestWebView#91b15831 flags:# from_bot_menu:flags.4?true silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON reply_to_msg_id:flags.0?int send_as:flags.13?InputPeer = WebViewResult;
messages.requestWebView#fc87a53c flags:# from_bot_menu:flags.4?true silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON platform:string reply_to_msg_id:flags.0?int send_as:flags.13?InputPeer = WebViewResult;
messages.prolongWebView#ea5fbcce flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to_msg_id:flags.0?int send_as:flags.13?InputPeer = Bool;
messages.requestSimpleWebView#6abb2f73 flags:# bot:InputUser url:string theme_params:flags.0?DataJSON = SimpleWebViewResult;
messages.requestSimpleWebView#299bec8e flags:# bot:InputUser url:string theme_params:flags.0?DataJSON platform:string = SimpleWebViewResult;
messages.sendWebViewResultMessage#a4314f5 bot_query_id:string result:InputBotInlineResult = WebViewMessageSent;
messages.sendWebViewData#dc0242c8 bot:InputUser random_id:long button_text:string data:string = Updates;
messages.transcribeAudio#269e9a49 peer:InputPeer msg_id:int = messages.TranscribedAudio;
@ -1699,6 +1742,10 @@ messages.rateTranscribedAudio#7f1d072f peer:InputPeer msg_id:int transcription_i
messages.getCustomEmojiDocuments#d9ab0f54 document_id:Vector<long> = Vector<Document>;
messages.getEmojiStickers#fbfca18f hash:long = messages.AllStickers;
messages.getFeaturedEmojiStickers#ecf6736 hash:long = messages.FeaturedStickers;
messages.reportReaction#3f64c076 peer:InputPeer id:int reaction_peer:InputPeer = Bool;
messages.getTopReactions#bb8125ba limit:int hash:long = messages.Reactions;
messages.getRecentReactions#39461db2 limit:int hash:long = messages.Reactions;
messages.clearRecentReactions#9dfeefb4 = Bool;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@ -1805,7 +1852,6 @@ payments.exportInvoice#f91b065 invoice_media:InputMedia = payments.ExportedInvoi
payments.assignAppStoreTransaction#80ed747d receipt:bytes purpose:InputStorePaymentPurpose = Updates;
payments.assignPlayMarketTransaction#dffd50d3 receipt:DataJSON purpose:InputStorePaymentPurpose = Updates;
payments.canPurchasePremium#9fc19eb6 purpose:InputStorePaymentPurpose = Bool;
payments.requestRecurringPayment#146e958d user_id:InputUser recurring_init_charge:string invoice_media:InputMedia = Updates;
stickers.createStickerSet#9021ab67 flags:# masks:flags.0?true animated:flags.1?true videos:flags.4?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector<InputStickerSetItem> software:flags.3?string = messages.StickerSet;
stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet;

View File

@ -194,7 +194,15 @@ std::string TD_TL_writer_java::gen_output_begin() const {
return "package " + package_name +
";\n\n"
"public class " +
tl_name + " {\n";
tl_name +
" {\n"
" static {\n"
" try {\n"
" System.loadLibrary(\"tdjni\");\n"
" } catch (UnsatisfiedLinkError e) {\n"
" e.printStackTrace();\n" +
" }\n"
" }\n\n";
}
std::string TD_TL_writer_java::gen_output_end() const {
@ -226,8 +234,11 @@ std::string TD_TL_writer_java::gen_class_begin(const std::string &class_name, co
full_class_name += "<" + fetched_type + ">";
}
std::string result = " public " + std::string(is_proxy ? "abstract " : "") + full_class_name + " {\n";
if (is_proxy) {
result += " public " + class_name + "() {\n }\n";
}
if (class_name == gen_base_tl_class_name() || class_name == gen_base_function_class_name()) {
result += " public native String toString();\n";
result += "\n public native String toString();\n";
}
return result;

View File

@ -49,11 +49,13 @@ class RawConnectionDefault final : public RawConnection {
bool can_send() const final {
return transport_->can_write();
}
TransportType get_transport_type() const final {
return transport_->get_type();
}
void send_crypto(const Storer &storer, int64 session_id, int64 salt, const AuthKey &auth_key,
uint64 quick_ack_token) final {
size_t send_crypto(const Storer &storer, int64 session_id, int64 salt, const AuthKey &auth_key,
uint64 quick_ack_token) final {
PacketInfo info;
info.version = 2;
info.no_crypto_flag = false;
@ -76,7 +78,9 @@ class RawConnectionDefault final : public RawConnection {
}
}
auto packet_size = packet.size();
transport_->write(std::move(packet), use_quick_ack);
return packet_size;
}
uint64 send_no_crypto(const Storer &storer) final {
@ -250,7 +254,7 @@ class RawConnectionDefault final : public RawConnection {
sync_with_poll(socket_fd_);
// read/write
// EINVAL may be returned in linux kernel < 2.6.28. And on some new kernels too.
// EINVAL can be returned in Linux kernel < 2.6.28. And on some new kernels too.
// just close connection and hope that read or write will not return this error too.
TRY_STATUS(socket_fd_.get_pending_error());
@ -280,11 +284,13 @@ class RawConnectionHttp final : public RawConnection {
bool can_send() const final {
return mode_ == Send;
}
TransportType get_transport_type() const final {
return mtproto::TransportType{mtproto::TransportType::Http, 0, mtproto::ProxySecret()};
}
void send_crypto(const Storer &storer, int64 session_id, int64 salt, const AuthKey &auth_key,
uint64 quick_ack_token) final {
size_t send_crypto(const Storer &storer, int64 session_id, int64 salt, const AuthKey &auth_key,
uint64 quick_ack_token) final {
PacketInfo info;
info.version = 2;
info.no_crypto_flag = false;
@ -295,7 +301,9 @@ class RawConnectionHttp final : public RawConnection {
auto packet = BufferWriter{Transport::write(storer, auth_key, &info), 0, 0};
Transport::write(storer, auth_key, &info, packet.as_slice());
auto packet_size = packet.size();
send_packet(packet.as_buffer_slice());
return packet_size;
}
uint64 send_no_crypto(const Storer &storer) final {

View File

@ -48,8 +48,8 @@ class RawConnection {
virtual bool can_send() const = 0;
virtual TransportType get_transport_type() const = 0;
virtual void send_crypto(const Storer &storer, int64 session_id, int64 salt, const AuthKey &auth_key,
uint64 quick_ack_token) = 0;
virtual size_t send_crypto(const Storer &storer, int64 session_id, int64 salt, const AuthKey &auth_key,
uint64 quick_ack_token) = 0;
virtual uint64 send_no_crypto(const Storer &storer) = 0;
virtual PollableFdInfo &get_poll_info() = 0;

View File

@ -717,14 +717,17 @@ Status SessionConnection::on_quick_ack(uint64 quick_ack_token) {
void SessionConnection::on_read(size_t size) {
last_read_at_ = Time::now_cached();
last_read_size_ += size;
}
SessionConnection::SessionConnection(Mode mode, unique_ptr<RawConnection> raw_connection, AuthData *auth_data)
: raw_connection_(std::move(raw_connection)), auth_data_(auth_data) {
: random_delay_(Random::fast(0, 5000000) * 1e-6)
, state_(Init)
, mode_(mode)
, created_at_(Time::now())
, raw_connection_(std::move(raw_connection))
, auth_data_(auth_data) {
CHECK(raw_connection_);
state_ = Init;
mode_ = mode;
created_at_ = Time::now();
}
PollableFdInfo &SessionConnection::get_poll_info() {
@ -765,8 +768,9 @@ void SessionConnection::do_close(Status status) {
void SessionConnection::send_crypto(const Storer &storer, uint64 quick_ack_token) {
CHECK(state_ != Closed);
raw_connection_->send_crypto(storer, auth_data_->get_session_id(), auth_data_->get_server_salt(Time::now_cached()),
auth_data_->get_auth_key(), quick_ack_token);
last_write_size_ += raw_connection_->send_crypto(storer, auth_data_->get_session_id(),
auth_data_->get_server_salt(Time::now_cached()),
auth_data_->get_auth_key(), quick_ack_token);
}
Result<uint64> SessionConnection::send_query(BufferSlice buffer, bool gzip_flag, int64 message_id,
@ -969,10 +973,11 @@ void SessionConnection::flush_packet() {
{
// LOG(ERROR) << (auth_data_->get_header().empty() ? '-' : '+');
uint64 parent_message_id = 0;
auto storer = PacketStorer<CryptoImpl>(
queries, auth_data_->get_header(), std::move(to_ack), ping_id, ping_disconnect_delay() + 2, max_delay,
max_after, max_wait, future_salt_n, to_get_state_info, to_resend_answer, to_cancel_answer, destroy_auth_key,
auth_data_, &container_id, &get_state_info_id, &resend_answer_id, &ping_message_id, &parent_message_id);
auto storer = PacketStorer<CryptoImpl>(queries, auth_data_->get_header(), std::move(to_ack), ping_id,
static_cast<int>(ping_disconnect_delay() + 2.0), max_delay, max_after,
max_wait, future_salt_n, to_get_state_info, to_resend_answer,
to_cancel_answer, destroy_auth_key, auth_data_, &container_id,
&get_state_info_id, &resend_answer_id, &ping_message_id, &parent_message_id);
auto quick_ack_token = use_quick_ack ? parent_message_id : 0;
send_crypto(storer, quick_ack_token);
@ -1032,7 +1037,18 @@ Status SessionConnection::do_flush() {
return Status::Error("No auth key");
}
TRY_STATUS(raw_connection_->flush(auth_data_->get_auth_key(), *this));
last_read_size_ = 0;
last_write_size_ = 0;
auto start_time = Time::now();
auto result = raw_connection_->flush(auth_data_->get_auth_key(), *this);
auto elapsed_time = Time::now() - start_time;
if (elapsed_time >= 0.01) {
LOG(ERROR) << "RawConnection::flush took " << elapsed_time << " seconds, written " << last_write_size_
<< " bytes, read " << last_read_size_ << " bytes and returned " << result;
}
if (result.is_error()) {
return result;
}
if (last_pong_at_ + ping_disconnect_delay() < Time::now_cached()) {
auto stats_callback = raw_connection_->stats_callback();

View File

@ -139,32 +139,31 @@ class SessionConnection final
bool is_main_ = false;
bool was_moved_ = false;
int rtt() const {
return max(2, static_cast<int>(raw_connection_->extra().rtt * 1.5 + 1));
double rtt() const {
return max(2.0, raw_connection_->extra().rtt * 1.5 + 1);
}
int32 read_disconnect_delay() const {
return online_flag_ ? rtt() * 7 / 2 : 135;
double read_disconnect_delay() const {
return online_flag_ ? rtt() * 3.5 : 135 + random_delay_;
}
int32 ping_disconnect_delay() const {
return (online_flag_ && is_main_) ? rtt() * 5 / 2 : 135;
double ping_disconnect_delay() const {
return online_flag_ && is_main_ ? rtt() * 2.5 : 135 + random_delay_;
}
int32 ping_may_delay() const {
return online_flag_ ? rtt() / 2 : 30;
double ping_may_delay() const {
return online_flag_ ? rtt() * 0.5 : 30 + random_delay_;
}
int32 ping_must_delay() const {
return online_flag_ ? rtt() : 60;
double ping_must_delay() const {
return online_flag_ ? rtt() : 60 + random_delay_;
}
double http_max_wait() const {
return 25.0; // 25s. Longer could be closed by proxy
}
static constexpr int HTTP_MAX_AFTER = 10; // 0.01s
static constexpr int HTTP_MAX_DELAY = 30; // 0.03s
static constexpr int TEMP_KEY_TIMEOUT = 60 * 60 * 24; // one day
static constexpr int HTTP_MAX_AFTER = 10; // 0.01s
static constexpr int HTTP_MAX_DELAY = 30; // 0.03s
vector<MtprotoQuery> to_send_;
vector<int64> to_ack_;
@ -182,6 +181,7 @@ class SessionConnection final
// nobody cleans up this map. But it should be really small.
FlatHashMap<uint64, vector<uint64>> container_to_service_msg_;
double random_delay_ = 0;
double last_read_at_ = 0;
double last_ping_at_ = 0;
double last_pong_at_ = 0;
@ -189,6 +189,9 @@ class SessionConnection final
uint64 last_ping_message_id_ = 0;
uint64 last_ping_container_id_ = 0;
uint64 last_read_size_ = 0;
uint64 last_write_size_ = 0;
bool need_destroy_auth_key_ = false;
bool sent_destroy_auth_key_ = false;

View File

@ -118,7 +118,7 @@ class SetAccountTtlQuery final : public Td::ResultHandler {
void send(int32 account_ttl) {
send_query(G()->net_query_creator().create(
telegram_api::account_setAccountTTL(make_tl_object<telegram_api::accountDaysTTL>(account_ttl))));
telegram_api::account_setAccountTTL(make_tl_object<telegram_api::accountDaysTTL>(account_ttl)), {{"me"}}));
}
void on_result(BufferSlice packet) final {
@ -315,7 +315,8 @@ class ChangeAuthorizationSettingsQuery final : public Td::ResultHandler {
flags |= telegram_api::account_changeAuthorizationSettings::CALL_REQUESTS_DISABLED_MASK;
}
send_query(G()->net_query_creator().create(telegram_api::account_changeAuthorizationSettings(
flags, hash, encrypted_requests_disabled, call_requests_disabled)));
flags, hash, encrypted_requests_disabled, call_requests_disabled),
{{"me"}}));
}
void on_result(BufferSlice packet) final {
@ -342,7 +343,8 @@ class SetAuthorizationTtlQuery final : public Td::ResultHandler {
}
void send(int32 authorization_ttl_days) {
send_query(G()->net_query_creator().create(telegram_api::account_setAuthorizationTTL(authorization_ttl_days)));
send_query(
G()->net_query_creator().create(telegram_api::account_setAuthorizationTTL(authorization_ttl_days), {{"me"}}));
}
void on_result(BufferSlice packet) final {
@ -472,7 +474,7 @@ class SetBotGroupDefaultAdminRightsQuery final : public Td::ResultHandler {
void send(AdministratorRights administrator_rights) {
send_query(G()->net_query_creator().create(
telegram_api::bots_setBotGroupDefaultAdminRights(administrator_rights.get_chat_admin_rights())));
telegram_api::bots_setBotGroupDefaultAdminRights(administrator_rights.get_chat_admin_rights()), {{"me"}}));
}
void on_result(BufferSlice packet) final {
@ -505,7 +507,7 @@ class SetBotBroadcastDefaultAdminRightsQuery final : public Td::ResultHandler {
void send(AdministratorRights administrator_rights) {
send_query(G()->net_query_creator().create(
telegram_api::bots_setBotBroadcastDefaultAdminRights(administrator_rights.get_chat_admin_rights())));
telegram_api::bots_setBotBroadcastDefaultAdminRights(administrator_rights.get_chat_admin_rights()), {{"me"}}));
}
void on_result(BufferSlice packet) final {

View File

@ -49,8 +49,8 @@ class RequestWebViewQuery final : public Td::ResultHandler {
}
void send(DialogId dialog_id, UserId bot_user_id, tl_object_ptr<telegram_api::InputUser> &&input_user, string &&url,
td_api::object_ptr<td_api::themeParameters> &&theme, MessageId reply_to_message_id, bool silent,
DialogId as_dialog_id) {
td_api::object_ptr<td_api::themeParameters> &&theme, string &&platform, MessageId reply_to_message_id,
bool silent, DialogId as_dialog_id) {
dialog_id_ = dialog_id;
bot_user_id_ = bot_user_id;
reply_to_message_id_ = reply_to_message_id;
@ -104,7 +104,8 @@ class RequestWebViewQuery final : public Td::ResultHandler {
send_query(G()->net_query_creator().create(telegram_api::messages_requestWebView(
flags, false /*ignored*/, false /*ignored*/, std::move(input_peer), std::move(input_user), url, start_parameter,
std::move(theme_parameters), reply_to_message_id.get_server_message_id().get(), std::move(as_input_peer))));
std::move(theme_parameters), platform, reply_to_message_id.get_server_message_id().get(),
std::move(as_input_peer))));
}
void on_result(BufferSlice packet) final {
@ -600,7 +601,7 @@ void AttachMenuManager::schedule_ping_web_view() {
void AttachMenuManager::request_web_view(DialogId dialog_id, UserId bot_user_id, MessageId reply_to_message_id,
string &&url, td_api::object_ptr<td_api::themeParameters> &&theme,
Promise<td_api::object_ptr<td_api::webAppInfo>> &&promise) {
string &&platform, Promise<td_api::object_ptr<td_api::webAppInfo>> &&promise) {
TRY_STATUS_PROMISE(promise, td_->contacts_manager_->get_bot_data(bot_user_id));
TRY_RESULT_PROMISE(promise, input_user, td_->contacts_manager_->get_input_user(bot_user_id));
@ -634,8 +635,8 @@ void AttachMenuManager::request_web_view(DialogId dialog_id, UserId bot_user_id,
DialogId as_dialog_id = td_->messages_manager_->get_dialog_default_send_message_as_dialog_id(dialog_id);
td_->create_handler<RequestWebViewQuery>(std::move(promise))
->send(dialog_id, bot_user_id, std::move(input_user), std::move(url), std::move(theme), reply_to_message_id,
silent, as_dialog_id);
->send(dialog_id, bot_user_id, std::move(input_user), std::move(url), std::move(theme), std::move(platform),
reply_to_message_id, silent, as_dialog_id);
}
void AttachMenuManager::open_web_view(int64 query_id, DialogId dialog_id, UserId bot_user_id,

View File

@ -33,7 +33,7 @@ class AttachMenuManager final : public Actor {
void init();
void request_web_view(DialogId dialog_id, UserId bot_user_id, MessageId reply_to_message_id, string &&url,
td_api::object_ptr<td_api::themeParameters> &&theme,
td_api::object_ptr<td_api::themeParameters> &&theme, string &&platform,
Promise<td_api::object_ptr<td_api::webAppInfo>> &&promise);
void open_web_view(int64 query_id, DialogId dialog_id, UserId bot_user_id, MessageId reply_to_message_id,

View File

@ -107,6 +107,12 @@ tl_object_ptr<td_api::AuthorizationState> AuthManager::get_authorization_state_o
switch (authorization_state) {
case State::WaitPhoneNumber:
return make_tl_object<td_api::authorizationStateWaitPhoneNumber>();
case State::WaitEmailAddress:
return make_tl_object<td_api::authorizationStateWaitEmailAddress>(allow_apple_id_, allow_google_id_);
case State::WaitEmailCode:
return make_tl_object<td_api::authorizationStateWaitEmailCode>(
allow_apple_id_, allow_google_id_, email_code_info_.get_email_address_authentication_code_info_object(),
next_phone_number_login_date_);
case State::WaitCode:
return send_code_helper_.get_authorization_state_wait_code();
case State::WaitQrCodeConfirmation:
@ -174,7 +180,8 @@ void AuthManager::check_bot_token(uint64 query_id, string bot_token) {
void AuthManager::request_qr_code_authentication(uint64 query_id, vector<UserId> other_user_ids) {
if (state_ != State::WaitPhoneNumber) {
if ((state_ == State::WaitCode || state_ == State::WaitPassword || state_ == State::WaitRegistration) &&
if ((state_ == State::WaitEmailAddress || state_ == State::WaitEmailCode || state_ == State::WaitCode ||
state_ == State::WaitPassword || state_ == State::WaitRegistration) &&
net_query_id_ == 0) {
// ok
} else {
@ -239,7 +246,8 @@ void AuthManager::on_update_login_token() {
void AuthManager::set_phone_number(uint64 query_id, string phone_number,
td_api::object_ptr<td_api::phoneNumberAuthenticationSettings> settings) {
if (state_ != State::WaitPhoneNumber) {
if ((state_ == State::WaitCode || state_ == State::WaitPassword || state_ == State::WaitRegistration) &&
if ((state_ == State::WaitEmailAddress || state_ == State::WaitEmailCode || state_ == State::WaitCode ||
state_ == State::WaitPassword || state_ == State::WaitRegistration) &&
net_query_id_ == 0) {
// ok
} else {
@ -251,12 +259,20 @@ void AuthManager::set_phone_number(uint64 query_id, string phone_number,
query_id, Status::Error(400, "Cannot set phone number after bot token was entered. You need to log out first"));
}
if (phone_number.empty()) {
return on_query_error(query_id, Status::Error(400, "Phone number can't be empty"));
return on_query_error(query_id, Status::Error(400, "Phone number must be non-empty"));
}
other_user_ids_.clear();
was_qr_code_request_ = false;
allow_apple_id_ = false;
allow_google_id_ = false;
email_address_ = {};
email_code_info_ = {};
next_phone_number_login_date_ = 0;
code_ = string();
email_code_ = {};
if (send_code_helper_.phone_number() != phone_number) {
send_code_helper_ = SendCodeHelper();
terms_of_service_ = TermsOfService();
@ -268,8 +284,35 @@ void AuthManager::set_phone_number(uint64 query_id, string phone_number,
std::move(phone_number), settings, api_id_, api_hash_)));
}
void AuthManager::set_email_address(uint64 query_id, string email_address) {
if (state_ != State::WaitEmailAddress) {
if (state_ == State::WaitEmailCode && net_query_id_ == 0) {
// ok
} else {
return on_query_error(query_id, Status::Error(400, "Call to setAuthenticationEmailAddress unexpected"));
}
}
if (email_address.empty()) {
return on_query_error(query_id, Status::Error(400, "Email address must be non-empty"));
}
email_address_ = std::move(email_address);
on_new_query(query_id);
start_net_query(NetQueryType::SendEmailCode,
G()->net_query_creator().create_unauth(send_code_helper_.send_verify_email_code(email_address_)));
}
void AuthManager::resend_authentication_code(uint64 query_id) {
if (state_ != State::WaitCode) {
if (state_ == State::WaitEmailCode) {
on_new_query(query_id);
start_net_query(NetQueryType::SendEmailCode,
G()->net_query_creator().create_unauth(send_code_helper_.send_verify_email_code(email_address_)));
return;
}
return on_query_error(query_id, Status::Error(400, "Call to resendAuthenticationCode unexpected"));
}
@ -283,16 +326,48 @@ void AuthManager::resend_authentication_code(uint64 query_id) {
start_net_query(NetQueryType::SendCode, G()->net_query_creator().create_unauth(r_resend_code.move_as_ok()));
}
void AuthManager::send_auth_sign_in_query() {
bool is_email = !email_code_.is_empty();
int32 flags =
is_email ? telegram_api::auth_signIn::EMAIL_VERIFICATION_MASK : telegram_api::auth_signIn::PHONE_CODE_MASK;
start_net_query(NetQueryType::SignIn,
G()->net_query_creator().create_unauth(telegram_api::auth_signIn(
flags, send_code_helper_.phone_number().str(), send_code_helper_.phone_code_hash().str(), code_,
is_email ? email_code_.get_input_email_verification() : nullptr)));
}
void AuthManager::check_email_code(uint64 query_id, EmailVerification &&code) {
if (code.is_empty()) {
return on_query_error(query_id, Status::Error(400, "Code must be non-empty"));
}
if (state_ != State::WaitEmailCode && !(state_ == State::WaitEmailAddress && code.is_email_code())) {
return on_query_error(query_id, Status::Error(400, "Call to checkAuthenticationEmailCode unexpected"));
}
code_ = string();
email_code_ = std::move(code);
on_new_query(query_id);
if (email_address_.empty()) {
send_auth_sign_in_query();
} else {
start_net_query(
NetQueryType::VerifyEmailAddress,
G()->net_query_creator().create_unauth(telegram_api::account_verifyEmail(
send_code_helper_.get_email_verify_purpose_login_setup(), email_code_.get_input_email_verification())));
}
}
void AuthManager::check_code(uint64 query_id, string code) {
if (state_ != State::WaitCode) {
return on_query_error(query_id, Status::Error(400, "Call to checkAuthenticationCode unexpected"));
}
code_ = std::move(code);
email_code_ = {};
on_new_query(query_id);
start_net_query(NetQueryType::SignIn,
G()->net_query_creator().create_unauth(telegram_api::auth_signIn(
send_code_helper_.phone_number().str(), send_code_helper_.phone_code_hash().str(), code_)));
send_auth_sign_in_query();
}
void AuthManager::register_user(uint64 query_id, string first_name, string last_name) {
@ -303,7 +378,7 @@ void AuthManager::register_user(uint64 query_id, string first_name, string last_
on_new_query(query_id);
first_name = clean_name(first_name, MAX_NAME_LENGTH);
if (first_name.empty()) {
return on_query_error(Status::Error(400, "First name can't be empty"));
return on_query_error(Status::Error(400, "First name must be non-empty"));
}
last_name = clean_name(last_name, MAX_NAME_LENGTH);
@ -389,7 +464,9 @@ void AuthManager::log_out(uint64 query_id) {
}
void AuthManager::send_log_out_query() {
auto query = G()->net_query_creator().create(telegram_api::auth_logOut());
// we can lose authorization while logging out, but still may need to resend the request,
// so we pretend that it doesn't require authorization
auto query = G()->net_query_creator().create_unauth(telegram_api::auth_logOut());
query->set_priority(1);
start_net_query(NetQueryType::LogOut, std::move(query));
}
@ -478,6 +555,33 @@ void AuthManager::start_net_query(NetQueryType net_query_type, NetQueryPtr net_q
G()->net_query_dispatcher().dispatch_with_callback(std::move(net_query), actor_shared(this));
}
void AuthManager::on_sent_code(telegram_api::object_ptr<telegram_api::auth_sentCode> &&sent_code) {
auto code_type_id = sent_code->type_->get_id();
if (code_type_id == telegram_api::auth_sentCodeTypeSetUpEmailRequired::ID) {
auto code_type = move_tl_object_as<telegram_api::auth_sentCodeTypeSetUpEmailRequired>(std::move(sent_code->type_));
send_code_helper_.on_phone_code_hash(std::move(sent_code->phone_code_hash_));
allow_apple_id_ = code_type->apple_signin_allowed_;
allow_google_id_ = code_type->google_signin_allowed_;
update_state(State::WaitEmailAddress, true);
} else if (code_type_id == telegram_api::auth_sentCodeTypeEmailCode::ID) {
auto code_type = move_tl_object_as<telegram_api::auth_sentCodeTypeEmailCode>(std::move(sent_code->type_));
send_code_helper_.on_phone_code_hash(std::move(sent_code->phone_code_hash_));
allow_apple_id_ = code_type->apple_signin_allowed_;
allow_google_id_ = code_type->google_signin_allowed_;
email_address_.clear();
email_code_info_ = SentEmailCode(std::move(code_type->email_pattern_), code_type->length_);
next_phone_number_login_date_ = td::max(static_cast<int32>(0), code_type->next_phone_login_date_);
if (email_code_info_.is_empty()) {
email_code_info_ = SentEmailCode("<unknown>", code_type->length_);
CHECK(!email_code_info_.is_empty());
}
update_state(State::WaitEmailCode, true);
} else {
send_code_helper_.on_sent_code(std::move(sent_code));
update_state(State::WaitCode, true);
}
}
void AuthManager::on_send_code_result(NetQueryPtr &result) {
auto r_sent_code = fetch_result<telegram_api::auth_sendCode>(result->ok());
if (r_sent_code.is_error()) {
@ -486,10 +590,43 @@ void AuthManager::on_send_code_result(NetQueryPtr &result) {
auto sent_code = r_sent_code.move_as_ok();
LOG(INFO) << "Receive " << to_string(sent_code);
on_sent_code(std::move(sent_code));
on_query_ok();
}
send_code_helper_.on_sent_code(std::move(sent_code));
void AuthManager::on_send_email_code_result(NetQueryPtr &result) {
auto r_sent_code = fetch_result<telegram_api::account_sendVerifyEmailCode>(result->ok());
if (r_sent_code.is_error()) {
return on_query_error(r_sent_code.move_as_error());
}
auto sent_code = r_sent_code.move_as_ok();
update_state(State::WaitCode, true);
LOG(INFO) << "Receive " << to_string(sent_code);
email_code_info_ = SentEmailCode(std::move(sent_code));
if (email_code_info_.is_empty()) {
return on_query_error(Status::Error(500, "Receive invalid response"));
}
next_phone_number_login_date_ = 0;
update_state(State::WaitEmailCode, true);
on_query_ok();
}
void AuthManager::on_verify_email_address_result(NetQueryPtr &result) {
auto r_email_verified = fetch_result<telegram_api::account_verifyEmail>(result->ok());
if (r_email_verified.is_error()) {
return on_query_error(r_email_verified.move_as_error());
}
auto email_verified = r_email_verified.move_as_ok();
LOG(INFO) << "Receive " << to_string(email_verified);
if (email_verified->get_id() != telegram_api::account_emailVerifiedLogin::ID) {
return on_query_error(Status::Error(500, "Receive invalid response"));
}
auto verified_login = telegram_api::move_object_as<telegram_api::account_emailVerifiedLogin>(email_verified);
on_sent_code(std::move(verified_login->sent_code_));
on_query_ok();
}
@ -615,9 +752,7 @@ void AuthManager::on_get_password_result(NetQueryPtr &result) {
set_login_token_expires_at(Time::now() + login_code_retry_delay_);
return;
} else {
start_net_query(NetQueryType::SignIn,
G()->net_query_creator().create_unauth(telegram_api::auth_signIn(
send_code_helper_.phone_number().str(), send_code_helper_.phone_code_hash().str(), code_)));
send_auth_sign_in_query();
return;
}
@ -857,8 +992,9 @@ void AuthManager::on_result(NetQueryPtr result) {
type = net_query_type_;
net_query_type_ = NetQueryType::None;
if (result->is_error()) {
if ((type == NetQueryType::SendCode || type == NetQueryType::SignIn || type == NetQueryType::RequestQrCode ||
type == NetQueryType::ImportQrCode) &&
if ((type == NetQueryType::SendCode || type == NetQueryType::SendEmailCode ||
type == NetQueryType::VerifyEmailAddress || type == NetQueryType::SignIn ||
type == NetQueryType::RequestQrCode || type == NetQueryType::ImportQrCode) &&
result->error().code() == 401 && result->error().message() == CSlice("SESSION_PASSWORD_NEEDED")) {
auto dc_id = DcId::main();
if (type == NetQueryType::ImportQrCode) {
@ -913,6 +1049,12 @@ void AuthManager::on_result(NetQueryPtr result) {
case NetQueryType::SendCode:
on_send_code_result(result);
break;
case NetQueryType::SendEmailCode:
on_send_email_code_result(result);
break;
case NetQueryType::VerifyEmailAddress:
on_verify_email_address_result(result);
break;
case NetQueryType::RequestQrCode:
on_request_qr_code_result(result, false);
break;
@ -988,6 +1130,8 @@ bool AuthManager::load_state() {
case State::WaitPassword:
case State::WaitRegistration:
return 86400;
case State::WaitEmailAddress:
case State::WaitEmailCode:
case State::WaitCode:
case State::WaitQrCodeConfirmation:
return 5 * 60;
@ -1003,7 +1147,18 @@ bool AuthManager::load_state() {
}
LOG(INFO) << "Load auth_state from database: " << tag("state", static_cast<int32>(db_state.state_));
if (db_state.state_ == State::WaitCode) {
if (db_state.state_ == State::WaitEmailAddress) {
allow_apple_id_ = db_state.allow_apple_id_;
allow_google_id_ = db_state.allow_google_id_;
send_code_helper_ = std::move(db_state.send_code_helper_);
} else if (db_state.state_ == State::WaitEmailCode) {
allow_apple_id_ = db_state.allow_apple_id_;
allow_google_id_ = db_state.allow_google_id_;
email_address_ = std::move(db_state.email_address_);
email_code_info_ = std::move(db_state.email_code_info_);
next_phone_number_login_date_ = db_state.next_phone_number_login_date_;
send_code_helper_ = std::move(db_state.send_code_helper_);
} else if (db_state.state_ == State::WaitCode) {
send_code_helper_ = std::move(db_state.send_code_helper_);
} else if (db_state.state_ == State::WaitQrCodeConfirmation) {
other_user_ids_ = std::move(db_state.other_user_ids_);
@ -1022,8 +1177,8 @@ bool AuthManager::load_state() {
}
void AuthManager::save_state() {
if (state_ != State::WaitCode && state_ != State::WaitQrCodeConfirmation && state_ != State::WaitPassword &&
state_ != State::WaitRegistration) {
if (state_ != State::WaitEmailAddress && state_ != State::WaitEmailCode && state_ != State::WaitCode &&
state_ != State::WaitQrCodeConfirmation && state_ != State::WaitPassword && state_ != State::WaitRegistration) {
if (state_ != State::Closing) {
G()->td_db()->get_binlog_pmc()->erase("auth_state");
}
@ -1031,7 +1186,12 @@ void AuthManager::save_state() {
}
DbState db_state = [&] {
if (state_ == State::WaitCode) {
if (state_ == State::WaitEmailAddress) {
return DbState::wait_email_address(api_id_, api_hash_, allow_apple_id_, allow_google_id_, send_code_helper_);
} else if (state_ == State::WaitEmailCode) {
return DbState::wait_email_code(api_id_, api_hash_, allow_apple_id_, allow_google_id_, email_address_,
email_code_info_, next_phone_number_login_date_, send_code_helper_);
} else if (state_ == State::WaitCode) {
return DbState::wait_code(api_id_, api_hash_, send_code_helper_);
} else if (state_ == State::WaitQrCodeConfirmation) {
return DbState::wait_qr_code_confirmation(api_id_, api_hash_, other_user_ids_, login_token_,

View File

@ -6,9 +6,11 @@
//
#pragma once
#include "td/telegram/EmailVerification.h"
#include "td/telegram/net/NetActor.h"
#include "td/telegram/net/NetQuery.h"
#include "td/telegram/SendCodeHelper.h"
#include "td/telegram/SentEmailCode.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/TermsOfService.h"
@ -35,7 +37,9 @@ class AuthManager final : public NetActor {
void set_phone_number(uint64 query_id, string phone_number,
td_api::object_ptr<td_api::phoneNumberAuthenticationSettings> settings);
void set_email_address(uint64 query_id, string email_address);
void resend_authentication_code(uint64 query_id);
void check_email_code(uint64 query_id, EmailVerification &&code);
void check_code(uint64 query_id, string code);
void register_user(uint64 query_id, string first_name, string last_name);
void request_qr_code_authentication(uint64 query_id, vector<UserId> other_user_ids);
@ -65,6 +69,8 @@ class AuthManager final : public NetActor {
WaitQrCodeConfirmation,
WaitPassword,
WaitRegistration,
WaitEmailAddress,
WaitEmailCode,
Ok,
LoggingOut,
DestroyingKeys,
@ -75,6 +81,8 @@ class AuthManager final : public NetActor {
SignIn,
SignUp,
SendCode,
SendEmailCode,
VerifyEmailAddress,
RequestQrCode,
ImportQrCode,
GetPassword,
@ -111,7 +119,16 @@ class AuthManager final : public NetActor {
string api_hash_;
Timestamp state_timestamp_;
// WaitCode
// WaitEmailAddress and WaitEmailCode
bool allow_apple_id_ = false;
bool allow_google_id_ = false;
// WaitEmailCode
string email_address_;
SentEmailCode email_code_info_;
int32 next_phone_number_login_date_ = 0;
// WaitEmailAddress, WaitEmailCode, WaitCode and WaitRegistration
SendCodeHelper send_code_helper_;
// WaitQrCodeConfirmation
@ -127,6 +144,28 @@ class AuthManager final : public NetActor {
DbState() = default;
static DbState wait_email_address(int32 api_id, string api_hash, bool allow_apple_id, bool allow_google_id,
SendCodeHelper send_code_helper) {
DbState state(State::WaitEmailAddress, api_id, std::move(api_hash));
state.send_code_helper_ = std::move(send_code_helper);
state.allow_apple_id_ = allow_apple_id;
state.allow_google_id_ = allow_google_id;
return state;
}
static DbState wait_email_code(int32 api_id, string api_hash, bool allow_apple_id, bool allow_google_id,
string email_address, SentEmailCode email_code_info,
int32 next_phone_number_login_date, SendCodeHelper send_code_helper) {
DbState state(State::WaitEmailCode, api_id, std::move(api_hash));
state.send_code_helper_ = std::move(send_code_helper);
state.allow_apple_id_ = allow_apple_id;
state.allow_google_id_ = allow_google_id;
state.email_address_ = std::move(email_address);
state.email_code_info_ = std::move(email_code_info);
state.next_phone_number_login_date_ = next_phone_number_login_date;
return state;
}
static DbState wait_code(int32 api_id, string api_hash, SendCodeHelper send_code_helper) {
DbState state(State::WaitCode, api_id, std::move(api_hash));
state.send_code_helper_ = std::move(send_code_helper);
@ -177,6 +216,16 @@ class AuthManager final : public NetActor {
int32 api_id_;
string api_hash_;
// State::WaitEmailAddress
bool allow_apple_id_ = false;
bool allow_google_id_ = false;
// State::WaitEmailCode
string email_address_;
SentEmailCode email_code_info_;
int32 next_phone_number_login_date_ = 0;
EmailVerification email_code_;
// State::WaitCode
SendCodeHelper send_code_helper_;
string code_;
@ -227,10 +276,15 @@ class AuthManager final : public NetActor {
void do_delete_account(uint64 query_id, string reason,
Result<tl_object_ptr<telegram_api::InputCheckPasswordSRP>> r_input_password);
void send_auth_sign_in_query();
void send_log_out_query();
void destroy_auth_keys();
void on_sent_code(telegram_api::object_ptr<telegram_api::auth_sentCode> &&sent_code);
void on_send_code_result(NetQueryPtr &result);
void on_send_email_code_result(NetQueryPtr &result);
void on_verify_email_address_result(NetQueryPtr &result);
void on_request_qr_code_result(NetQueryPtr &result, bool is_import);
void on_get_password_result(NetQueryPtr &result);
void on_request_password_recovery_result(NetQueryPtr &result);

View File

@ -55,6 +55,7 @@ void AuthManager::DbState::store(StorerT &storer) const {
bool is_wait_registration_supported = true;
bool is_wait_registration_stores_phone_number = true;
bool is_wait_qr_code_confirmation_supported = true;
bool is_time_store_supported = true;
BEGIN_STORE_FLAGS();
STORE_FLAG(has_terms_of_service);
STORE_FLAG(is_pbkdf2_supported);
@ -62,17 +63,27 @@ void AuthManager::DbState::store(StorerT &storer) const {
STORE_FLAG(is_wait_registration_supported);
STORE_FLAG(is_wait_registration_stores_phone_number);
STORE_FLAG(is_wait_qr_code_confirmation_supported);
STORE_FLAG(allow_apple_id_);
STORE_FLAG(allow_google_id_);
STORE_FLAG(is_time_store_supported);
END_STORE_FLAGS();
store(state_, storer);
store(api_id_, storer);
store(api_hash_, storer);
store(state_timestamp_, storer);
store_time(state_timestamp_.at(), storer);
if (has_terms_of_service) {
store(terms_of_service_, storer);
}
if (state_ == State::WaitCode) {
if (state_ == State::WaitEmailAddress) {
store(send_code_helper_, storer);
} else if (state_ == State::WaitEmailCode) {
store(send_code_helper_, storer);
store(email_address_, storer);
store(email_code_info_, storer);
store(next_phone_number_login_date_, storer);
} else if (state_ == State::WaitCode) {
store(send_code_helper_, storer);
} else if (state_ == State::WaitQrCodeConfirmation) {
store(other_user_ids_, storer);
@ -96,6 +107,7 @@ void AuthManager::DbState::parse(ParserT &parser) {
bool is_wait_registration_supported = false;
bool is_wait_registration_stores_phone_number = false;
bool is_wait_qr_code_confirmation_supported = false;
bool is_time_store_supported = false;
if (parser.version() >= static_cast<int32>(Version::AddTermsOfService)) {
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_terms_of_service);
@ -104,26 +116,39 @@ void AuthManager::DbState::parse(ParserT &parser) {
PARSE_FLAG(is_wait_registration_supported);
PARSE_FLAG(is_wait_registration_stores_phone_number);
PARSE_FLAG(is_wait_qr_code_confirmation_supported);
PARSE_FLAG(allow_apple_id_);
PARSE_FLAG(allow_google_id_);
PARSE_FLAG(is_time_store_supported);
END_PARSE_FLAGS();
}
if (!is_wait_qr_code_confirmation_supported) {
return parser.set_error("Have no QR code confirmation support");
if (!is_time_store_supported) {
return parser.set_error("Have no time store support");
}
CHECK(is_pbkdf2_supported);
CHECK(is_srp_supported);
CHECK(is_wait_registration_supported);
CHECK(is_wait_registration_stores_phone_number);
CHECK(is_wait_qr_code_confirmation_supported);
parse(state_, parser);
parse(api_id_, parser);
parse(api_hash_, parser);
parse(state_timestamp_, parser);
double state_timestamp = 0.0;
parse_time(state_timestamp, parser);
state_timestamp_ = Timestamp::at(state_timestamp);
if (has_terms_of_service) {
parse(terms_of_service_, parser);
}
if (state_ == State::WaitCode) {
if (state_ == State::WaitEmailAddress) {
parse(send_code_helper_, parser);
} else if (state_ == State::WaitEmailCode) {
parse(send_code_helper_, parser);
parse(email_address_, parser);
parse(email_code_info_, parser);
parse(next_phone_number_login_date_, parser);
} else if (state_ == State::WaitCode) {
parse(send_code_helper_, parser);
} else if (state_ == State::WaitQrCodeConfirmation) {
parse(other_user_ids_, parser);

View File

@ -1,61 +0,0 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/AvailableReaction.h"
#include "td/utils/algorithm.h"
namespace td {
AvailableReactionType get_reaction_type(const vector<AvailableReaction> &available_reactions, const string &reaction) {
for (auto &available_reaction : available_reactions) {
if (available_reaction.reaction_ == reaction) {
if (available_reaction.is_premium_) {
return AvailableReactionType::NeedsPremium;
}
return AvailableReactionType::Available;
}
}
return AvailableReactionType::Unavailable;
}
vector<string> get_active_reactions(const vector<string> &available_reactions,
const vector<AvailableReaction> &active_reactions) {
if (available_reactions.empty()) {
// fast path
return available_reactions;
}
if (available_reactions.size() == active_reactions.size()) {
size_t i;
for (i = 0; i < available_reactions.size(); i++) {
if (available_reactions[i] != active_reactions[i].reaction_) {
break;
}
}
if (i == available_reactions.size()) {
// fast path
return available_reactions;
}
}
vector<string> result;
for (const auto &active_reaction : active_reactions) {
if (td::contains(available_reactions, active_reaction.reaction_)) {
result.push_back(active_reaction.reaction_);
}
}
return result;
}
td_api::object_ptr<td_api::availableReaction> AvailableReaction::get_available_reaction_object() const {
return td_api::make_object<td_api::availableReaction>(reaction_, is_premium_);
}
bool operator==(const AvailableReaction &lhs, const AvailableReaction &rhs) {
return lhs.reaction_ == rhs.reaction_ && lhs.is_premium_ == rhs.is_premium_;
}
} // namespace td

View File

@ -1,38 +0,0 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/td_api.h"
#include "td/utils/common.h"
namespace td {
struct AvailableReaction {
string reaction_;
bool is_premium_;
AvailableReaction(const string &reaction, bool is_premium) : reaction_(reaction), is_premium_(is_premium) {
}
td_api::object_ptr<td_api::availableReaction> get_available_reaction_object() const;
};
bool operator==(const AvailableReaction &lhs, const AvailableReaction &rhs);
inline bool operator!=(const AvailableReaction &lhs, const AvailableReaction &rhs) {
return !(lhs == rhs);
}
enum class AvailableReactionType : int32 { Unavailable, Available, NeedsPremium };
AvailableReactionType get_reaction_type(const vector<AvailableReaction> &reactions, const string &reaction);
vector<string> get_active_reactions(const vector<string> &available_reactions,
const vector<AvailableReaction> &active_reactions);
} // namespace td

View File

@ -133,7 +133,7 @@ void set_menu_button(Td *td, UserId user_id, td_api::object_ptr<td_api::botMenuB
input_bot_menu_button = telegram_api::make_object<telegram_api::botMenuButtonCommands>();
} else if (menu_button->text_.empty()) {
if (menu_button->url_ != "default") {
return promise.set_error(Status::Error(400, "Menu button text can't be empty"));
return promise.set_error(Status::Error(400, "Menu button text must be non-empty"));
}
input_bot_menu_button = telegram_api::make_object<telegram_api::botMenuButtonDefault>();
} else {

View File

@ -66,8 +66,9 @@ void CallManager::create_call(UserId user_id, tl_object_ptr<telegram_api::InputU
auto call_id = create_call_actor();
auto actor = get_call_actor(call_id);
CHECK(!actor.empty());
auto safe_promise = SafePromise<CallId>(std::move(promise), Status::Error(400, "Call not found"));
send_closure(actor, &CallActor::create_call, user_id, std::move(input_user), std::move(protocol), is_video,
std::move(promise));
std::move(safe_promise));
}
void CallManager::accept_call(CallId call_id, CallProtocol &&protocol, Promise<Unit> promise) {
@ -75,7 +76,8 @@ void CallManager::accept_call(CallId call_id, CallProtocol &&protocol, Promise<U
if (actor.empty()) {
return promise.set_error(Status::Error(400, "Call not found"));
}
send_closure(actor, &CallActor::accept_call, std::move(protocol), std::move(promise));
auto safe_promise = SafePromise<Unit>(std::move(promise), Status::Error(400, "Call not found"));
send_closure(actor, &CallActor::accept_call, std::move(protocol), std::move(safe_promise));
}
void CallManager::send_call_signaling_data(CallId call_id, string &&data, Promise<Unit> promise) {
@ -83,7 +85,8 @@ void CallManager::send_call_signaling_data(CallId call_id, string &&data, Promis
if (actor.empty()) {
return promise.set_error(Status::Error(400, "Call not found"));
}
send_closure(actor, &CallActor::send_call_signaling_data, std::move(data), std::move(promise));
auto safe_promise = SafePromise<Unit>(std::move(promise), Status::Error(400, "Call not found"));
send_closure(actor, &CallActor::send_call_signaling_data, std::move(data), std::move(safe_promise));
}
void CallManager::discard_call(CallId call_id, bool is_disconnected, int32 duration, bool is_video, int64 connection_id,
@ -92,7 +95,9 @@ void CallManager::discard_call(CallId call_id, bool is_disconnected, int32 durat
if (actor.empty()) {
return promise.set_error(Status::Error(400, "Call not found"));
}
send_closure(actor, &CallActor::discard_call, is_disconnected, duration, is_video, connection_id, std::move(promise));
auto safe_promise = SafePromise<Unit>(std::move(promise), Status::Error(400, "Call not found"));
send_closure(actor, &CallActor::discard_call, is_disconnected, duration, is_video, connection_id,
std::move(safe_promise));
}
void CallManager::rate_call(CallId call_id, int32 rating, string comment,
@ -101,7 +106,8 @@ void CallManager::rate_call(CallId call_id, int32 rating, string comment,
if (actor.empty()) {
return promise.set_error(Status::Error(400, "Call not found"));
}
send_closure(actor, &CallActor::rate_call, rating, std::move(comment), std::move(problems), std::move(promise));
auto safe_promise = SafePromise<Unit>(std::move(promise), Status::Error(400, "Call not found"));
send_closure(actor, &CallActor::rate_call, rating, std::move(comment), std::move(problems), std::move(safe_promise));
}
void CallManager::send_call_debug_information(CallId call_id, string data, Promise<Unit> promise) {
@ -109,7 +115,8 @@ void CallManager::send_call_debug_information(CallId call_id, string data, Promi
if (actor.empty()) {
return promise.set_error(Status::Error(400, "Call not found"));
}
send_closure(actor, &CallActor::send_call_debug_information, std::move(data), std::move(promise));
auto safe_promise = SafePromise<Unit>(std::move(promise), Status::Error(400, "Call not found"));
send_closure(actor, &CallActor::send_call_debug_information, std::move(data), std::move(safe_promise));
}
void CallManager::send_call_log(CallId call_id, td_api::object_ptr<td_api::InputFile> log_file, Promise<Unit> promise) {
@ -117,7 +124,8 @@ void CallManager::send_call_log(CallId call_id, td_api::object_ptr<td_api::Input
if (actor.empty()) {
return promise.set_error(Status::Error(400, "Call not found"));
}
send_closure(actor, &CallActor::send_call_log, std::move(log_file), std::move(promise));
auto safe_promise = SafePromise<Unit>(std::move(promise), Status::Error(400, "Call not found"));
send_closure(actor, &CallActor::send_call_log, std::move(log_file), std::move(safe_promise));
}
CallId CallManager::create_call_actor() {

View File

@ -0,0 +1,118 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/ChatReactions.h"
#include "td/telegram/MessageReaction.h"
#include "td/utils/algorithm.h"
namespace td {
ChatReactions::ChatReactions(telegram_api::object_ptr<telegram_api::ChatReactions> &&chat_reactions_ptr) {
if (chat_reactions_ptr == nullptr) {
return;
}
switch (chat_reactions_ptr->get_id()) {
case telegram_api::chatReactionsNone::ID:
break;
case telegram_api::chatReactionsAll::ID: {
auto chat_reactions = move_tl_object_as<telegram_api::chatReactionsAll>(chat_reactions_ptr);
allow_all_ = true;
allow_custom_ = chat_reactions->allow_custom_;
break;
}
case telegram_api::chatReactionsSome::ID: {
auto chat_reactions = move_tl_object_as<telegram_api::chatReactionsSome>(chat_reactions_ptr);
reactions_ =
transform(chat_reactions->reactions_, [](const telegram_api::object_ptr<telegram_api::Reaction> &reaction) {
return get_message_reaction_string(reaction);
});
break;
}
default:
UNREACHABLE();
}
}
ChatReactions::ChatReactions(td_api::object_ptr<td_api::ChatAvailableReactions> &&chat_reactions_ptr,
bool allow_custom) {
if (chat_reactions_ptr == nullptr) {
return;
}
switch (chat_reactions_ptr->get_id()) {
case td_api::chatAvailableReactionsAll::ID:
allow_all_ = true;
allow_custom_ = allow_custom;
break;
case td_api::chatAvailableReactionsSome::ID: {
auto chat_reactions = move_tl_object_as<td_api::chatAvailableReactionsSome>(chat_reactions_ptr);
reactions_ = transform(chat_reactions->reactions_, [](const td_api::object_ptr<td_api::ReactionType> &reaction) {
return get_message_reaction_string(reaction);
});
break;
}
default:
UNREACHABLE();
}
}
ChatReactions ChatReactions::get_active_reactions(const FlatHashMap<string, size_t> &active_reaction_pos) const {
ChatReactions result = *this;
if (!reactions_.empty()) {
CHECK(!allow_all_);
CHECK(!allow_custom_);
td::remove_if(result.reactions_,
[&](const string &reaction) { return !is_active_reaction(reaction, active_reaction_pos); });
}
return result;
}
bool ChatReactions::is_allowed_reaction(const string &reaction) const {
CHECK(!allow_all_);
if (allow_custom_ && is_custom_reaction(reaction)) {
return true;
}
return td::contains(reactions_, reaction);
}
td_api::object_ptr<td_api::ChatAvailableReactions> ChatReactions::get_chat_available_reactions_object() const {
if (allow_all_) {
return td_api::make_object<td_api::chatAvailableReactionsAll>();
}
return td_api::make_object<td_api::chatAvailableReactionsSome>(transform(reactions_, get_reaction_type_object));
}
telegram_api::object_ptr<telegram_api::ChatReactions> ChatReactions::get_input_chat_reactions() const {
if (allow_all_) {
int32 flags = 0;
if (allow_custom_) {
flags |= telegram_api::chatReactionsAll::ALLOW_CUSTOM_MASK;
}
return telegram_api::make_object<telegram_api::chatReactionsAll>(flags, false /*ignored*/);
}
if (!reactions_.empty()) {
return telegram_api::make_object<telegram_api::chatReactionsSome>(transform(reactions_, get_input_reaction));
}
return telegram_api::make_object<telegram_api::chatReactionsNone>();
}
bool operator==(const ChatReactions &lhs, const ChatReactions &rhs) {
// don't compare allow_custom_
return lhs.reactions_ == rhs.reactions_ && lhs.allow_all_ == rhs.allow_all_;
}
StringBuilder &operator<<(StringBuilder &string_builder, const ChatReactions &reactions) {
if (reactions.allow_all_) {
if (reactions.allow_custom_) {
return string_builder << "AllReactions";
}
return string_builder << "AllRegularReactions";
}
return string_builder << '[' << reactions.reactions_ << ']';
}
} // namespace td

View File

@ -0,0 +1,83 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/StringBuilder.h"
#include "td/utils/tl_helpers.h"
namespace td {
struct ChatReactions {
vector<string> reactions_;
bool allow_all_ = false; // implies empty reactions
bool allow_custom_ = false; // implies allow_all
ChatReactions() = default;
explicit ChatReactions(vector<string> &&reactions) : reactions_(std::move(reactions)) {
}
explicit ChatReactions(telegram_api::object_ptr<telegram_api::ChatReactions> &&chat_reactions_ptr);
ChatReactions(td_api::object_ptr<td_api::ChatAvailableReactions> &&chat_reactions_ptr, bool allow_custom);
ChatReactions(bool allow_all, bool allow_custom) : allow_all_(allow_all), allow_custom_(allow_custom) {
}
ChatReactions get_active_reactions(const FlatHashMap<string, size_t> &active_reaction_pos) const;
bool is_allowed_reaction(const string &reaction) const;
telegram_api::object_ptr<telegram_api::ChatReactions> get_input_chat_reactions() const;
td_api::object_ptr<td_api::ChatAvailableReactions> get_chat_available_reactions_object() const;
bool empty() const {
return reactions_.empty() && !allow_all_;
}
template <class StorerT>
void store(StorerT &storer) const {
bool has_reactions = !reactions_.empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(allow_all_);
STORE_FLAG(allow_custom_);
STORE_FLAG(has_reactions);
END_STORE_FLAGS();
if (has_reactions) {
td::store(reactions_, storer);
}
}
template <class ParserT>
void parse(ParserT &parser) {
bool has_reactions;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(allow_all_);
PARSE_FLAG(allow_custom_);
PARSE_FLAG(has_reactions);
END_PARSE_FLAGS();
if (has_reactions) {
td::parse(reactions_, parser);
}
}
};
bool operator==(const ChatReactions &lhs, const ChatReactions &rhs);
inline bool operator!=(const ChatReactions &lhs, const ChatReactions &rhs) {
return !(lhs == rhs);
}
StringBuilder &operator<<(StringBuilder &string_builder, const ChatReactions &reactions);
} // namespace td

View File

@ -98,8 +98,7 @@ class ClientManager::Impl final {
CHECK(concurrent_scheduler_ == nullptr);
CHECK(options_.net_query_stats == nullptr);
options_.net_query_stats = std::make_shared<NetQueryStats>();
concurrent_scheduler_ = make_unique<ConcurrentScheduler>();
concurrent_scheduler_->init(0);
concurrent_scheduler_ = make_unique<ConcurrentScheduler>(0, 0);
concurrent_scheduler_->start();
}
tds_[client_id] =
@ -354,8 +353,7 @@ class MultiImpl {
static constexpr int32 ADDITIONAL_THREAD_COUNT = 3;
explicit MultiImpl(std::shared_ptr<NetQueryStats> net_query_stats) {
concurrent_scheduler_ = std::make_shared<ConcurrentScheduler>();
concurrent_scheduler_->init(ADDITIONAL_THREAD_COUNT);
concurrent_scheduler_ = std::make_shared<ConcurrentScheduler>(ADDITIONAL_THREAD_COUNT, 0);
concurrent_scheduler_->start();
{
@ -423,6 +421,7 @@ class MultiImpl {
static std::atomic<uint32> current_id_;
};
constexpr int32 MultiImpl::ADDITIONAL_THREAD_COUNT;
std::atomic<uint32> MultiImpl::current_id_{1};
class MultiImplPool {

View File

@ -12,6 +12,7 @@
#include "td/telegram/JsonValue.h"
#include "td/telegram/LinkManager.h"
#include "td/telegram/logevent/LogEvent.h"
#include "td/telegram/MessageReaction.h"
#include "td/telegram/net/AuthDataShared.h"
#include "td/telegram/net/ConnectionCreator.h"
#include "td/telegram/net/DcId.h"
@ -1414,6 +1415,13 @@ void ConfigManager::process_config(tl_object_ptr<telegram_api::config> config) {
options.set_option_integer("notification_cloud_delay_ms", fix_timeout_ms(config->notify_cloud_delay_ms_));
options.set_option_integer("notification_default_delay_ms", fix_timeout_ms(config->notify_default_delay_ms_));
if (is_from_main_dc && !options.have_option("default_reaction_need_sync")) {
auto reaction_str = get_message_reaction_string(config->reactions_default_);
if (!reaction_str.empty()) {
options.set_option_string("default_reaction", reaction_str);
}
}
// delete outdated options
options.set_option_empty("suggested_language_code");
options.set_option_empty("chat_big_size");
@ -1470,11 +1478,10 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
string animation_search_emojis;
vector<SuggestedAction> suggested_actions;
bool can_archive_and_mute_new_chats_from_unknown_users = false;
int64 chat_read_mark_expire_period = 0;
int64 chat_read_mark_size_threshold = 0;
int32 chat_read_mark_expire_period = 0;
int32 chat_read_mark_size_threshold = 0;
double animated_emoji_zoom = 0.0;
string default_reaction;
int64 reactions_uniq_max = 0;
int32 reactions_uniq_max = 0;
vector<string> premium_features;
auto &premium_limit_keys = get_premium_limit_keys();
string premium_bot_username;
@ -1722,10 +1729,6 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
chat_read_mark_size_threshold = get_json_value_int(std::move(key_value->value_), key);
continue;
}
if (key == "reactions_default") {
default_reaction = get_json_value_string(std::move(key_value->value_), key);
continue;
}
if (key == "reactions_uniq_max") {
reactions_uniq_max = get_json_value_int(std::move(key_value->value_), key);
continue;
@ -1798,6 +1801,16 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
stickers_normal_by_emoji_per_premium_num = get_json_value_int(std::move(key_value->value_), key);
continue;
}
if (key == "default_emoji_statuses_stickerset_id") {
auto setting_value = get_json_value_long(std::move(key_value->value_), key);
G()->set_option_integer("themed_emoji_statuses_sticker_set_id", setting_value);
continue;
}
if (key == "reactions_user_max_default" || key == "reactions_user_max_premium") {
auto setting_value = get_json_value_int(std::move(key_value->value_), key);
G()->set_option_integer(key, setting_value);
continue;
}
new_values.push_back(std::move(key_value));
}
@ -1814,13 +1827,15 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
if (ignored_restriction_reasons.empty()) {
options.set_option_empty("ignored_restriction_reasons");
if (options.get_option_boolean("ignore_sensitive_content_restrictions", true)) {
if (options.get_option_boolean("ignore_sensitive_content_restrictions", true) ||
options.get_option_boolean("can_ignore_sensitive_content_restrictions", true)) {
get_content_settings(Auto());
}
} else {
options.set_option_string("ignored_restriction_reasons", ignored_restriction_reasons);
if (!options.get_option_boolean("can_ignore_sensitive_content_restrictions")) {
if (!options.get_option_boolean("can_ignore_sensitive_content_restrictions") ||
!options.get_option_boolean("ignore_sensitive_content_restrictions")) {
get_content_settings(Auto());
}
}
@ -1872,9 +1887,6 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
} else {
options.set_option_integer("chat_read_mark_size_threshold", chat_read_mark_size_threshold);
}
if (!options.have_option("default_reaction_need_sync")) {
options.set_option_string("default_reaction", default_reaction);
}
if (reactions_uniq_max <= 0 || reactions_uniq_max == 11) {
options.set_option_empty("reactions_uniq_max");
} else {

View File

@ -463,8 +463,10 @@ class UploadProfilePhotoQuery final : public Td::ResultHandler {
flags |= telegram_api::photos_uploadProfilePhoto::FILE_MASK;
photo_input_file = std::move(input_file);
}
send_query(G()->net_query_creator().create(telegram_api::photos_uploadProfilePhoto(
flags, std::move(photo_input_file), std::move(video_input_file), main_frame_timestamp)));
send_query(G()->net_query_creator().create(
telegram_api::photos_uploadProfilePhoto(flags, std::move(photo_input_file), std::move(video_input_file),
main_frame_timestamp),
{{"me"}}));
}
void on_result(BufferSlice packet) final {
@ -502,7 +504,8 @@ class UpdateProfilePhotoQuery final : public Td::ResultHandler {
file_id_ = file_id;
old_photo_id_ = old_photo_id;
file_reference_ = FileManager::extract_file_reference(input_photo);
send_query(G()->net_query_creator().create(telegram_api::photos_updateProfilePhoto(std::move(input_photo))));
send_query(
G()->net_query_creator().create(telegram_api::photos_updateProfilePhoto(std::move(input_photo)), {{"me"}}));
}
void on_result(BufferSlice packet) final {
@ -593,8 +596,8 @@ class UpdateProfileQuery final : public Td::ResultHandler {
first_name_ = first_name;
last_name_ = last_name;
about_ = about;
send_query(
G()->net_query_creator().create(telegram_api::account_updateProfile(flags, first_name, last_name, about)));
send_query(G()->net_query_creator().create(telegram_api::account_updateProfile(flags, first_name, last_name, about),
{{"me"}}));
}
void on_result(BufferSlice packet) final {
@ -648,7 +651,7 @@ class UpdateUsernameQuery final : public Td::ResultHandler {
}
void send(const string &username) {
send_query(G()->net_query_creator().create(telegram_api::account_updateUsername(username)));
send_query(G()->net_query_creator().create(telegram_api::account_updateUsername(username), {{"me"}}));
}
void on_result(BufferSlice packet) final {
@ -671,6 +674,38 @@ class UpdateUsernameQuery final : public Td::ResultHandler {
}
};
class UpdateEmojiStatusQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit UpdateEmojiStatusQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(EmojiStatus emoji_status) {
send_query(G()->net_query_creator().create(
telegram_api::account_updateEmojiStatus(emoji_status.get_input_emoji_status()), {{"me"}}));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_updateEmojiStatus>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
LOG(DEBUG) << "Receive result for UpdateEmojiStatusQuery: " << result_ptr.ok();
if (result_ptr.ok()) {
promise_.set_value(Unit());
} else {
promise_.set_error(Status::Error(400, "Failed to change Premium badge"));
}
}
void on_error(Status status) final {
get_recent_emoji_statuses(td_, Auto());
promise_.set_error(std::move(status));
}
};
class CheckChannelUsernameQuery final : public Td::ResultHandler {
Promise<bool> promise_;
ChannelId channel_id_;
@ -2554,11 +2589,9 @@ class GetInactiveChannelsQuery final : public Td::ResultHandler {
auto result = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for GetInactiveChannelsQuery: " << to_string(result);
// TODO use result->dates_
// don't need to use result->dates_, because chat.last_message.date is more reliable
td_->contacts_manager_->on_get_users(std::move(result->users_), "GetInactiveChannelsQuery");
td_->contacts_manager_->on_get_inactive_channels(std::move(result->chats_));
promise_.set_value(Unit());
td_->contacts_manager_->on_get_inactive_channels(std::move(result->chats_), std::move(promise_));
}
void on_error(Status status) final {
@ -3382,6 +3415,9 @@ ContactsManager::ContactsManager(Td *td, ActorShared<> parent) : td_(td), parent
user_online_timeout_.set_callback(on_user_online_timeout_callback);
user_online_timeout_.set_callback_data(static_cast<void *>(this));
user_emoji_status_timeout_.set_callback(on_user_emoji_status_timeout_callback);
user_emoji_status_timeout_.set_callback_data(static_cast<void *>(this));
channel_unban_timeout_.set_callback(on_channel_unban_timeout_callback);
channel_unban_timeout_.set_callback_data(static_cast<void *>(this));
@ -3471,6 +3507,28 @@ void ContactsManager::on_user_online_timeout(UserId user_id) {
update_user_online_member_count(u);
}
void ContactsManager::on_user_emoji_status_timeout_callback(void *contacts_manager_ptr, int64 user_id_long) {
if (G()->close_flag()) {
return;
}
auto contacts_manager = static_cast<ContactsManager *>(contacts_manager_ptr);
send_closure_later(contacts_manager->actor_id(contacts_manager), &ContactsManager::on_user_emoji_status_timeout,
UserId(user_id_long));
}
void ContactsManager::on_user_emoji_status_timeout(UserId user_id) {
if (G()->close_flag()) {
return;
}
auto u = get_user(user_id);
CHECK(u != nullptr);
CHECK(u->is_update_user_sent);
update_user(u, user_id);
}
void ContactsManager::on_channel_unban_timeout_callback(void *contacts_manager_ptr, int64 channel_id_long) {
if (G()->close_flag()) {
return;
@ -3622,6 +3680,7 @@ void ContactsManager::User::store(StorerT &storer) const {
bool has_cache_version = cache_version != 0;
bool has_is_contact = true;
bool has_restriction_reasons = !restriction_reasons.empty();
bool has_emoji_status = !emoji_status.is_empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(is_received);
STORE_FLAG(is_verified);
@ -3650,6 +3709,7 @@ void ContactsManager::User::store(StorerT &storer) const {
STORE_FLAG(can_be_added_to_attach_menu);
STORE_FLAG(is_premium); // 25
STORE_FLAG(attach_menu_enabled);
STORE_FLAG(has_emoji_status);
END_STORE_FLAGS();
store(first_name, storer);
if (has_last_name) {
@ -3681,6 +3741,9 @@ void ContactsManager::User::store(StorerT &storer) const {
if (has_cache_version) {
store(cache_version, storer);
}
if (has_emoji_status) {
store(emoji_status, storer);
}
}
template <class ParserT>
@ -3695,6 +3758,7 @@ void ContactsManager::User::parse(ParserT &parser) {
bool has_cache_version;
bool has_is_contact;
bool has_restriction_reasons;
bool has_emoji_status;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(is_received);
PARSE_FLAG(is_verified);
@ -3723,6 +3787,7 @@ void ContactsManager::User::parse(ParserT &parser) {
PARSE_FLAG(can_be_added_to_attach_menu);
PARSE_FLAG(is_premium);
PARSE_FLAG(attach_menu_enabled);
PARSE_FLAG(has_emoji_status);
END_PARSE_FLAGS();
parse(first_name, parser);
if (has_last_name) {
@ -3774,6 +3839,9 @@ void ContactsManager::User::parse(ParserT &parser) {
if (has_cache_version) {
parse(cache_version, parser);
}
if (has_emoji_status) {
parse(emoji_status, parser);
}
if (!check_utf8(first_name)) {
LOG(ERROR) << "Have invalid first name \"" << first_name << '"';
@ -6556,6 +6624,32 @@ void ContactsManager::set_username(const string &username, Promise<Unit> &&promi
td_->create_handler<UpdateUsernameQuery>(std::move(promise))->send(username);
}
void ContactsManager::set_emoji_status(EmojiStatus emoji_status, Promise<Unit> &&promise) {
if (!td_->option_manager_->get_option_boolean("is_premium")) {
return promise.set_error(Status::Error(400, "The method is available only for Telegram Premium users"));
}
add_recent_emoji_status(td_, emoji_status);
auto query_promise = PromiseCreator::lambda(
[actor_id = actor_id(this), emoji_status, promise = std::move(promise)](Result<Unit> result) mutable {
if (result.is_ok()) {
send_closure(actor_id, &ContactsManager::on_set_emoji_status, emoji_status, std::move(promise));
} else {
promise.set_error(result.move_as_error());
}
});
td_->create_handler<UpdateEmojiStatusQuery>(std::move(query_promise))->send(emoji_status);
}
void ContactsManager::on_set_emoji_status(EmojiStatus emoji_status, Promise<Unit> &&promise) {
auto user_id = get_my_id();
User *u = get_user(user_id);
if (u != nullptr) {
on_update_user_emoji_status(u, user_id, emoji_status);
update_user(u, user_id);
}
promise.set_value(Unit());
}
void ContactsManager::set_chat_description(ChatId chat_id, const string &description, Promise<Unit> &&promise) {
auto new_description = strip_empty_characters(description, MAX_DESCRIPTION_LENGTH);
auto c = get_chat(chat_id);
@ -7150,7 +7244,9 @@ void ContactsManager::add_channel_participant(ChannelId channel_id, UserId user_
return promise.set_error(Status::Error(400, "Can't return to kicked from chat"));
}
speculative_add_channel_user(channel_id, user_id, DialogParticipantStatus::Member(), c->status);
if (!get_channel_join_request(c)) {
speculative_add_channel_user(channel_id, user_id, DialogParticipantStatus::Member(), c->status);
}
td_->create_handler<JoinChannelQuery>(std::move(promise))->send(channel_id);
return;
}
@ -8210,26 +8306,42 @@ void ContactsManager::update_dialogs_for_discussion(DialogId dialog_id, bool is_
}
vector<DialogId> ContactsManager::get_inactive_channels(Promise<Unit> &&promise) {
if (inactive_channels_inited_) {
if (inactive_channel_ids_inited_) {
promise.set_value(Unit());
return transform(inactive_channels_, [&](ChannelId channel_id) {
DialogId dialog_id{channel_id};
td_->messages_manager_->force_create_dialog(dialog_id, "get_inactive_channels");
return dialog_id;
});
return transform(inactive_channel_ids_, [&](ChannelId channel_id) { return DialogId(channel_id); });
}
td_->create_handler<GetInactiveChannelsQuery>(std::move(promise))->send();
return {};
}
void ContactsManager::on_get_inactive_channels(vector<tl_object_ptr<telegram_api::Chat>> &&chats) {
inactive_channels_inited_ = true;
inactive_channels_ = get_channel_ids(std::move(chats), "on_get_inactive_channels");
void ContactsManager::on_get_inactive_channels(vector<tl_object_ptr<telegram_api::Chat>> &&chats,
Promise<Unit> &&promise) {
auto channel_ids = get_channel_ids(std::move(chats), "on_get_inactive_channels");
MultiPromiseActorSafe mpas{"GetInactiveChannelsMultiPromiseActor"};
mpas.add_promise(PromiseCreator::lambda([actor_id = actor_id(this), channel_ids,
promise = std::move(promise)](Unit) mutable {
send_closure(actor_id, &ContactsManager::on_create_inactive_channels, std::move(channel_ids), std::move(promise));
}));
mpas.set_ignore_errors(true);
auto lock_promise = mpas.get_promise();
for (auto channel_id : channel_ids) {
td_->messages_manager_->create_dialog(DialogId(channel_id), false, mpas.get_promise());
}
lock_promise.set_value(Unit());
}
void ContactsManager::on_create_inactive_channels(vector<ChannelId> &&channel_ids, Promise<Unit> &&promise) {
inactive_channel_ids_inited_ = true;
inactive_channel_ids_ = std::move(channel_ids);
promise.set_value(Unit());
}
void ContactsManager::remove_inactive_channel(ChannelId channel_id) {
if (inactive_channels_inited_ && td::remove(inactive_channels_, channel_id)) {
if (inactive_channel_ids_inited_ && td::remove(inactive_channel_ids_, channel_id)) {
LOG(DEBUG) << "Remove " << channel_id << " from list of inactive channels";
}
}
@ -8676,6 +8788,7 @@ void ContactsManager::on_get_user(tl_object_ptr<telegram_api::User> &&user_ptr,
on_update_user_name(u, user_id, std::move(user->first_name_), std::move(user->last_name_),
std::move(user->username_));
}
on_update_user_emoji_status(u, user_id, EmojiStatus(std::move(user->emoji_status_)));
bool is_verified = (flags & USER_FLAG_IS_VERIFIED) != 0;
bool is_premium = (flags & USER_FLAG_IS_PREMIUM) != 0;
@ -8724,17 +8837,16 @@ void ContactsManager::on_get_user(tl_object_ptr<telegram_api::User> &&user_ptr,
<< "Receive not bot " << user_id << " which has bot info version from " << source;
int32 bot_info_version = has_bot_info_version ? user->bot_info_version_ : -1;
if (is_verified != u->is_verified || is_premium != u->is_premium || is_support != u->is_support ||
is_bot != u->is_bot || can_join_groups != u->can_join_groups ||
can_read_all_group_messages != u->can_read_all_group_messages || restriction_reasons != u->restriction_reasons ||
is_scam != u->is_scam || is_fake != u->is_fake || is_inline_bot != u->is_inline_bot ||
inline_query_placeholder != u->inline_query_placeholder || need_location_bot != u->need_location_bot ||
can_be_added_to_attach_menu != u->can_be_added_to_attach_menu || attach_menu_enabled != u->attach_menu_enabled) {
if (is_verified != u->is_verified || is_support != u->is_support || is_bot != u->is_bot ||
can_join_groups != u->can_join_groups || can_read_all_group_messages != u->can_read_all_group_messages ||
restriction_reasons != u->restriction_reasons || is_scam != u->is_scam || is_fake != u->is_fake ||
is_inline_bot != u->is_inline_bot || inline_query_placeholder != u->inline_query_placeholder ||
need_location_bot != u->need_location_bot || can_be_added_to_attach_menu != u->can_be_added_to_attach_menu ||
attach_menu_enabled != u->attach_menu_enabled) {
LOG_IF(ERROR, is_bot != u->is_bot && !is_deleted && !u->is_deleted && u->is_received)
<< "User.is_bot has changed for " << user_id << "/" << u->username << " from " << source << " from "
<< u->is_bot << " to " << is_bot;
u->is_verified = is_verified;
u->is_premium = is_premium;
u->is_support = is_support;
u->is_bot = is_bot;
u->can_join_groups = can_join_groups;
@ -8751,6 +8863,10 @@ void ContactsManager::on_get_user(tl_object_ptr<telegram_api::User> &&user_ptr,
LOG(DEBUG) << "Info has changed for " << user_id;
u->is_changed = true;
}
if (is_premium != u->is_premium) {
u->is_premium = is_premium;
u->is_changed = true;
}
if (u->bot_info_version != bot_info_version) {
u->bot_info_version = bot_info_version;
@ -9082,7 +9198,7 @@ ContactsManager::User *ContactsManager::get_user_force(UserId user_id) {
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, user_id.get(), 1, first_name, string(), username,
phone_number, std::move(profile_photo), nullptr, bot_info_version, Auto(), string(), string());
phone_number, std::move(profile_photo), nullptr, bot_info_version, Auto(), string(), string(), nullptr);
on_get_user(std::move(user), "get_user_force");
u = get_user(user_id);
CHECK(u != nullptr && u->is_received);
@ -10221,6 +10337,7 @@ void ContactsManager::update_user(User *u, UserId user_id, bool from_binlog, boo
if (td_->option_manager_->get_option_boolean("is_premium") != u->is_premium) {
td_->option_manager_->set_option_boolean("is_premium", u->is_premium);
send_closure(td_->config_manager_, &ConfigManager::request_config, true);
td_->stickers_manager_->reload_top_reactions();
}
}
if (u->is_name_changed || u->is_username_changed || u->is_is_contact_changed) {
@ -10289,6 +10406,27 @@ void ContactsManager::update_user(User *u, UserId user_id, bool from_binlog, boo
}
}
auto unix_time = G()->unix_time();
auto effective_custom_emoji_id = u->emoji_status.get_effective_custom_emoji_id(u->is_premium, unix_time);
if (effective_custom_emoji_id != u->last_sent_emoji_status) {
u->last_sent_emoji_status = effective_custom_emoji_id;
u->is_changed = true;
} else {
u->need_save_to_database = true;
}
if (u->last_sent_emoji_status != 0) {
auto until_date = u->emoji_status.get_until_date();
auto left_time = until_date - unix_time;
if (left_time >= 0 && left_time < 30 * 86400) {
LOG(DEBUG) << "Set emoji status timeout for " << user_id << " in " << left_time;
user_emoji_status_timeout_.set_timeout_in(user_id.get(), left_time);
} else {
user_emoji_status_timeout_.cancel_timeout(user_id.get());
}
} else {
user_emoji_status_timeout_.cancel_timeout(user_id.get());
}
if (u->is_deleted) {
td_->inline_queries_manager_->remove_recent_inline_bot(user_id, Promise<>());
}
@ -10831,9 +10969,7 @@ void ContactsManager::on_get_user_full(tl_object_ptr<telegram_api::userFull> &&u
bool supports_video_calls = user->video_calls_available_ && !user->phone_calls_private_;
bool has_private_calls = user->phone_calls_private_;
bool voice_messages_forbidden = u->is_premium ? user->voice_messages_forbidden_ : false;
auto premium_gift_options = transform(std::move(user->premium_gifts_), [](auto &&premium_gift_option) {
return PremiumGiftOption(std::move(premium_gift_option));
});
auto premium_gift_options = get_premium_gift_options(std::move(user->premium_gifts_));
AdministratorRights group_administrator_rights(user->bot_group_admin_rights_, ChannelType::Megagroup);
AdministratorRights broadcast_administrator_rights(user->bot_broadcast_admin_rights_, ChannelType::Broadcast);
if (user_full->can_be_called != can_be_called || user_full->supports_video_calls != supports_video_calls ||
@ -11691,6 +11827,29 @@ void ContactsManager::register_user_photo(User *u, UserId user_id, const Photo &
}
}
void ContactsManager::on_update_user_emoji_status(UserId user_id,
tl_object_ptr<telegram_api::EmojiStatus> &&emoji_status) {
if (!user_id.is_valid()) {
LOG(ERROR) << "Receive invalid " << user_id;
return;
}
User *u = get_user_force(user_id);
if (u != nullptr) {
on_update_user_emoji_status(u, user_id, EmojiStatus(std::move(emoji_status)));
update_user(u, user_id);
} else {
LOG(INFO) << "Ignore update user emoji status about unknown " << user_id;
}
}
void ContactsManager::on_update_user_emoji_status(User *u, UserId user_id, EmojiStatus emoji_status) {
if (u->emoji_status != emoji_status) {
LOG(DEBUG) << "Change emoji status of " << user_id << " from " << u->emoji_status << " to " << emoji_status;
u->emoji_status = emoji_status;
}
}
void ContactsManager::on_update_user_is_contact(User *u, UserId user_id, bool is_contact, bool is_mutual_contact) {
UserId my_id = get_my_id();
if (user_id == my_id) {
@ -16658,8 +16817,8 @@ td_api::object_ptr<td_api::UserStatus> ContactsManager::get_user_status_object(U
td_api::object_ptr<td_api::updateUser> ContactsManager::get_update_unknown_user_object(UserId user_id) {
return td_api::make_object<td_api::updateUser>(td_api::make_object<td_api::user>(
user_id.get(), "", "", "", "", td_api::make_object<td_api::userStatusEmpty>(), nullptr, false, false, false,
false, false, "", false, false, false, td_api::make_object<td_api::userTypeUnknown>(), "", false));
user_id.get(), "", "", "", "", td_api::make_object<td_api::userStatusEmpty>(), nullptr, nullptr, false, false,
false, false, false, "", false, false, false, td_api::make_object<td_api::userTypeUnknown>(), "", false));
}
int64 ContactsManager::get_user_id_object(UserId user_id, const char *source) const {
@ -16716,11 +16875,13 @@ tl_object_ptr<td_api::user> ContactsManager::get_user_object(UserId user_id, con
type = make_tl_object<td_api::userTypeRegular>();
}
auto emoji_status = u->last_sent_emoji_status != 0 ? u->emoji_status.get_emoji_status_object() : nullptr;
return make_tl_object<td_api::user>(
user_id.get(), u->first_name, u->last_name, u->username, u->phone_number, get_user_status_object(user_id, u),
get_profile_photo_object(td_->file_manager_.get(), u->photo), u->is_contact, u->is_mutual_contact, u->is_verified,
u->is_premium, u->is_support, get_restriction_reason_description(u->restriction_reasons), u->is_scam, u->is_fake,
u->is_received, std::move(type), u->language_code, u->attach_menu_enabled);
get_profile_photo_object(td_->file_manager_.get(), u->photo), std::move(emoji_status), u->is_contact,
u->is_mutual_contact, u->is_verified, u->is_premium, u->is_support,
get_restriction_reason_description(u->restriction_reasons), u->is_scam, u->is_fake, u->is_received,
std::move(type), u->language_code, u->attach_menu_enabled);
}
vector<int64> ContactsManager::get_user_ids_object(const vector<UserId> &user_ids, const char *source) const {
@ -16779,18 +16940,14 @@ tl_object_ptr<td_api::userFullInfo> ContactsManager::get_user_full_info_object(U
}
bio_object = get_formatted_text_object(bio, true, 0);
}
auto base_premium_gift_it =
std::min_element(user_full->premium_gift_options.begin(), user_full->premium_gift_options.end());
auto premium_gift_options = transform(user_full->premium_gift_options, [&base_premium_gift_it](const auto &option) {
return option.get_premium_gift_option_object(*base_premium_gift_it);
});
auto voice_messages_forbidden = is_premium ? user_full->voice_messages_forbidden : false;
return make_tl_object<td_api::userFullInfo>(
get_chat_photo_object(td_->file_manager_.get(), user_full->photo), user_full->is_blocked,
user_full->can_be_called, user_full->supports_video_calls, user_full->has_private_calls,
!user_full->private_forward_name.empty(), voice_messages_forbidden,
user_full->need_phone_number_privacy_exception, std::move(bio_object), std::move(premium_gift_options),
user_full->common_chat_count, std::move(bot_info));
return make_tl_object<td_api::userFullInfo>(get_chat_photo_object(td_->file_manager_.get(), user_full->photo),
user_full->is_blocked, user_full->can_be_called,
user_full->supports_video_calls, user_full->has_private_calls,
!user_full->private_forward_name.empty(), voice_messages_forbidden,
user_full->need_phone_number_privacy_exception, std::move(bio_object),
get_premium_payment_options_object(user_full->premium_gift_options),
user_full->common_chat_count, std::move(bot_info));
}
td_api::object_ptr<td_api::updateBasicGroup> ContactsManager::get_update_unknown_basic_group_object(ChatId chat_id) {

View File

@ -19,6 +19,7 @@
#include "td/telegram/DialogLocation.h"
#include "td/telegram/DialogParticipant.h"
#include "td/telegram/DialogParticipantFilter.h"
#include "td/telegram/EmojiStatus.h"
#include "td/telegram/files/FileId.h"
#include "td/telegram/files/FileSourceId.h"
#include "td/telegram/FolderId.h"
@ -180,6 +181,7 @@ class ContactsManager final : public Actor {
void on_update_user_name(UserId user_id, string &&first_name, string &&last_name, string &&username);
void on_update_user_phone_number(UserId user_id, string &&phone_number);
void on_update_user_photo(UserId user_id, tl_object_ptr<telegram_api::UserProfilePhoto> &&photo_ptr);
void on_update_user_emoji_status(UserId user_id, tl_object_ptr<telegram_api::EmojiStatus> &&emoji_status);
void on_update_user_online(UserId user_id, tl_object_ptr<telegram_api::UserStatus> &&status);
void on_update_user_local_was_online(UserId user_id, int32 local_was_online);
void on_update_user_is_blocked(UserId user_id, bool is_blocked);
@ -252,7 +254,7 @@ class ContactsManager final : public Actor {
void on_get_dialogs_for_discussion(vector<tl_object_ptr<telegram_api::Chat>> &&chats);
void on_get_inactive_channels(vector<tl_object_ptr<telegram_api::Chat>> &&chats);
void on_get_inactive_channels(vector<tl_object_ptr<telegram_api::Chat>> &&chats, Promise<Unit> &&promise);
void remove_inactive_channel(ChannelId channel_id);
@ -345,6 +347,8 @@ class ContactsManager final : public Actor {
void set_username(const string &username, Promise<Unit> &&promise);
void set_emoji_status(EmojiStatus emoji_status, Promise<Unit> &&promise);
void set_chat_description(ChatId chat_id, const string &description, Promise<Unit> &&promise);
void set_channel_username(ChannelId channel_id, const string &username, Promise<Unit> &&promise);
@ -643,6 +647,8 @@ class ContactsManager final : public Actor {
string username;
string phone_number;
int64 access_hash = -1;
EmojiStatus emoji_status;
int64 last_sent_emoji_status = 0;
ProfilePhoto photo;
@ -1072,6 +1078,7 @@ class ContactsManager final : public Actor {
static constexpr int32 USER_FLAG_IS_ATTACH_MENU_BOT = 1 << 27;
static constexpr int32 USER_FLAG_IS_PREMIUM = 1 << 28;
static constexpr int32 USER_FLAG_ATTACH_MENU_ENABLED = 1 << 29;
static constexpr int32 USER_FLAG_HAS_EMOJI_STATUS = 1 << 30;
static constexpr int32 USER_FULL_FLAG_IS_BLOCKED = 1 << 0;
static constexpr int32 USER_FULL_FLAG_HAS_ABOUT = 1 << 1;
@ -1248,10 +1255,13 @@ class ContactsManager final : public Actor {
static bool is_valid_username(const string &username);
void on_set_emoji_status(EmojiStatus emoji_status, Promise<Unit> &&promise);
void on_update_user_name(User *u, UserId user_id, string &&first_name, string &&last_name, string &&username);
void on_update_user_phone_number(User *u, UserId user_id, string &&phone_number);
void on_update_user_photo(User *u, UserId user_id, tl_object_ptr<telegram_api::UserProfilePhoto> &&photo,
const char *source);
void on_update_user_emoji_status(User *u, UserId user_id, EmojiStatus emoji_status);
void on_update_user_is_contact(User *u, UserId user_id, bool is_contact, bool is_mutual_contact);
void on_update_user_online(User *u, UserId user_id, tl_object_ptr<telegram_api::UserStatus> &&status);
void on_update_user_local_was_online(User *u, UserId user_id, int32 local_was_online);
@ -1604,6 +1614,8 @@ class ContactsManager final : public Actor {
vector<DialogId> get_dialog_ids(vector<tl_object_ptr<telegram_api::Chat>> &&chats, const char *source);
void on_create_inactive_channels(vector<ChannelId> &&channel_ids, Promise<Unit> &&promise);
void update_dialogs_for_discussion(DialogId dialog_id, bool is_suitable);
void send_edit_chat_admin_query(ChatId chat_id, UserId user_id, bool is_administrator, Promise<Unit> &&promise);
@ -1672,6 +1684,8 @@ class ContactsManager final : public Actor {
static void on_user_online_timeout_callback(void *contacts_manager_ptr, int64 user_id_long);
static void on_user_emoji_status_timeout_callback(void *contacts_manager_ptr, int64 user_id_long);
static void on_channel_unban_timeout_callback(void *contacts_manager_ptr, int64 channel_id_long);
static void on_user_nearby_timeout_callback(void *contacts_manager_ptr, int64 user_id_long);
@ -1684,6 +1698,8 @@ class ContactsManager final : public Actor {
void on_user_online_timeout(UserId user_id);
void on_user_emoji_status_timeout(UserId user_id);
void on_channel_unban_timeout(ChannelId channel_id);
void on_user_nearby_timeout(UserId user_id);
@ -1749,8 +1765,8 @@ class ContactsManager final : public Actor {
bool dialogs_for_discussion_inited_ = false;
vector<DialogId> dialogs_for_discussion_;
bool inactive_channels_inited_ = false;
vector<ChannelId> inactive_channels_;
bool inactive_channel_ids_inited_ = false;
vector<ChannelId> inactive_channel_ids_;
FlatHashMap<UserId, vector<Promise<Unit>>, UserIdHash> load_user_from_database_queries_;
FlatHashSet<UserId, UserIdHash> loaded_from_database_users_;
@ -1857,6 +1873,7 @@ class ContactsManager final : public Actor {
vector<int32> unimported_contact_invites_; // result of change_imported_contacts
MultiTimeout user_online_timeout_{"UserOnlineTimeout"};
MultiTimeout user_emoji_status_timeout_{"UserEmojiStatusTimeout"};
MultiTimeout channel_unban_timeout_{"ChannelUnbanTimeout"};
MultiTimeout user_nearby_timeout_{"UserNearbyTimeout"};
MultiTimeout slow_mode_delay_timeout_{"SlowModeDelayTimeout"};

View File

@ -7,6 +7,7 @@
#include "td/telegram/DialogEventLog.h"
#include "td/telegram/ChannelId.h"
#include "td/telegram/ChatReactions.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/DialogInviteLink.h"
#include "td/telegram/DialogLocation.h"
@ -151,9 +152,8 @@ static td_api::object_ptr<td_api::ChatEventAction> get_chat_event_action_object(
}
case telegram_api::channelAdminLogEventActionUpdatePinned::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionUpdatePinned>(action_ptr);
DialogId sender_dialog_id;
auto message = td->messages_manager_->get_dialog_event_log_message_object(
DialogId(channel_id), std::move(action->message_), sender_dialog_id);
DialogId(channel_id), std::move(action->message_), actor_dialog_id);
if (message == nullptr) {
return nullptr;
}
@ -346,8 +346,11 @@ static td_api::object_ptr<td_api::ChatEventAction> get_chat_event_action_object(
}
case telegram_api::channelAdminLogEventActionChangeAvailableReactions::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionChangeAvailableReactions>(action_ptr);
return td_api::make_object<td_api::chatEventAvailableReactionsChanged>(std::move(action->prev_value_),
std::move(action->new_value_));
ChatReactions old_available_reactions(std::move(action->prev_value_));
ChatReactions new_available_reactions(std::move(action->new_value_));
return td_api::make_object<td_api::chatEventAvailableReactionsChanged>(
old_available_reactions.get_chat_available_reactions_object(),
new_available_reactions.get_chat_available_reactions_object());
}
default:
UNREACHABLE();

View File

@ -120,7 +120,14 @@ struct DialogListIdHash {
inline StringBuilder &operator<<(StringBuilder &string_builder, DialogListId dialog_list_id) {
if (dialog_list_id.is_folder()) {
return string_builder << "chat list " << dialog_list_id.get_folder_id();
auto folder_id = dialog_list_id.get_folder_id();
if (folder_id == FolderId::archive()) {
return string_builder << "Archive chat list";
}
if (folder_id == FolderId::main()) {
return string_builder << "Main chat list";
}
return string_builder << "chat list " << folder_id;
}
if (dialog_list_id.is_filter()) {
return string_builder << "chat list " << dialog_list_id.get_filter_id();

View File

@ -110,7 +110,7 @@ class DownloadManagerImpl final : public DownloadManager {
}
void remove_file(FileId file_id, FileSourceId file_source_id, bool delete_from_cache, Promise<Unit> promise) final {
promise.set_result(remove_file_impl(file_id, file_source_id, delete_from_cache));
promise.set_result(remove_file_impl(file_id, file_source_id, delete_from_cache, "remove_file"));
}
void remove_file_if_finished(FileId file_id) final {
@ -131,7 +131,7 @@ class DownloadManagerImpl final : public DownloadManager {
to_remove.push_back(file_info.file_id);
}
for (auto file_id : to_remove) {
remove_file_impl(file_id, {}, delete_from_cache);
remove_file_impl(file_id, {}, delete_from_cache, "remove_all_files");
}
promise.set_value(Unit());
}
@ -140,7 +140,7 @@ class DownloadManagerImpl final : public DownloadManager {
Promise<td_api::object_ptr<td_api::file>> promise) final {
TRY_STATUS_PROMISE(promise, check_is_active("add_file"));
remove_file_impl(file_id, {}, false);
remove_file_impl(file_id, {}, false, "add_file");
auto download_id = next_download_id();
@ -301,19 +301,6 @@ class DownloadManagerImpl final : public DownloadManager {
}
}
void update_file_deleted(FileId internal_file_id) final {
if (!callback_ || !is_database_loaded_) {
return;
}
auto r_file_info_ptr = get_file_info_by_internal(internal_file_id);
if (r_file_info_ptr.is_error()) {
return;
}
auto &file_info = *r_file_info_ptr.ok();
remove_file_impl(file_info.file_id, {}, false);
}
void update_file_viewed(FileId file_id, FileSourceId file_source_id) final {
if (unviewed_completed_download_ids_.empty() || !callback_ || !is_database_loaded_) {
return;
@ -517,7 +504,7 @@ class DownloadManagerImpl final : public DownloadManager {
if (r_search_text.is_error()) {
if (!G()->close_flag()) {
remove_file_impl(it->second->file_id, {}, false);
remove_file_impl(it->second->file_id, {}, false, "add_download_to_hints");
}
} else {
auto search_text = r_search_text.move_as_ok();
@ -572,9 +559,9 @@ class DownloadManagerImpl final : public DownloadManager {
}
}
Status remove_file_impl(FileId file_id, FileSourceId file_source_id, bool delete_from_cache) {
Status remove_file_impl(FileId file_id, FileSourceId file_source_id, bool delete_from_cache, const char *source) {
LOG(INFO) << "Remove from downloads file " << file_id << " from " << file_source_id;
TRY_STATUS(check_is_active("remove_file_impl"));
TRY_STATUS(check_is_active(source));
TRY_RESULT(file_info_ptr, get_file_info(file_id, file_source_id));
auto &file_info = *file_info_ptr;
auto download_id = file_info.download_id;
@ -608,7 +595,7 @@ class DownloadManagerImpl final : public DownloadManager {
if (!is_completed(*file_info_ptr)) {
return Status::Error("File is active");
}
return remove_file_impl(file_id, {}, false);
return remove_file_impl(file_id, {}, false, "remove_file_if_finished_impl");
}
void timeout_expired() final {
@ -775,11 +762,15 @@ class DownloadManagerImpl final : public DownloadManager {
}
void check_completed_downloads_size() {
if (!is_database_loaded_) {
return;
}
constexpr size_t MAX_COMPLETED_DOWNLOADS = 200;
while (completed_download_ids_.size() > MAX_COMPLETED_DOWNLOADS) {
auto download_id = *completed_download_ids_.begin();
auto file_info = get_file_info(download_id).move_as_ok();
remove_file_impl(file_info->file_id, FileSourceId(), false);
remove_file_impl(file_info->file_id, FileSourceId(), false, "check_completed_downloads_size");
}
}

View File

@ -106,7 +106,6 @@ class DownloadManager : public Actor {
virtual void remove_file_if_finished(FileId file_id) = 0;
virtual void update_file_download_state(FileId internal_file_id, int64 downloaded_size, int64 size,
int64 expected_size, bool is_paused) = 0;
virtual void update_file_deleted(FileId internal_file_id) = 0;
virtual void update_file_viewed(FileId file_id, FileSourceId file_source_id) = 0;
};

View File

@ -0,0 +1,53 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/EmailVerification.h"
#include "td/telegram/misc.h"
namespace td {
EmailVerification::EmailVerification(td_api::object_ptr<td_api::EmailAddressAuthentication> &&code) {
if (code == nullptr) {
return;
}
switch (code->get_id()) {
case td_api::emailAddressAuthenticationCode::ID:
type_ = Type::Code;
code_ = static_cast<const td_api::emailAddressAuthenticationCode *>(code.get())->code_;
break;
case td_api::emailAddressAuthenticationAppleId::ID:
type_ = Type::Apple;
code_ = static_cast<const td_api::emailAddressAuthenticationAppleId *>(code.get())->token_;
break;
case td_api::emailAddressAuthenticationGoogleId::ID:
type_ = Type::Google;
code_ = static_cast<const td_api::emailAddressAuthenticationGoogleId *>(code.get())->token_;
break;
default:
UNREACHABLE();
break;
}
if (!clean_input_string(code_)) {
*this = {};
}
}
telegram_api::object_ptr<telegram_api::EmailVerification> EmailVerification::get_input_email_verification() const {
switch (type_) {
case Type::Code:
return telegram_api::make_object<telegram_api::emailVerificationCode>(code_);
case Type::Apple:
return telegram_api::make_object<telegram_api::emailVerificationApple>(code_);
case Type::Google:
return telegram_api::make_object<telegram_api::emailVerificationGoogle>(code_);
default:
UNREACHABLE();
return nullptr;
}
}
} // namespace td

View File

@ -0,0 +1,37 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/utils/common.h"
namespace td {
class EmailVerification {
enum class Type : int32 { None, Code, Apple, Google };
Type type_ = Type::None;
string code_;
public:
EmailVerification() = default;
explicit EmailVerification(td_api::object_ptr<td_api::EmailAddressAuthentication> &&code);
telegram_api::object_ptr<telegram_api::EmailVerification> get_input_email_verification() const;
bool is_empty() const {
return type_ == Type::None;
}
bool is_email_code() const {
return type_ == Type::Code;
}
};
} // namespace td

327
td/telegram/EmojiStatus.cpp Normal file
View File

@ -0,0 +1,327 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/EmojiStatus.h"
#include "td/telegram/Global.h"
#include "td/telegram/logevent/LogEvent.h"
#include "td/telegram/StickersManager.h"
#include "td/telegram/Td.h"
#include "td/telegram/TdDb.h"
#include "td/utils/algorithm.h"
#include "td/utils/buffer.h"
#include "td/utils/Status.h"
namespace td {
struct EmojiStatuses {
int64 hash_ = 0;
vector<EmojiStatus> emoji_statuses_;
td_api::object_ptr<td_api::emojiStatuses> get_emoji_statuses_object() const {
auto emoji_statuses = transform(emoji_statuses_, [](const EmojiStatus &emoji_status) {
CHECK(!emoji_status.is_empty());
return emoji_status.get_emoji_status_object();
});
return td_api::make_object<td_api::emojiStatuses>(std::move(emoji_statuses));
}
EmojiStatuses() = default;
explicit EmojiStatuses(tl_object_ptr<telegram_api::account_emojiStatuses> &&emoji_statuses) {
CHECK(emoji_statuses != nullptr);
hash_ = emoji_statuses->hash_;
for (auto &status : emoji_statuses->statuses_) {
EmojiStatus emoji_status(std::move(status));
if (emoji_status.is_empty()) {
LOG(ERROR) << "Receive empty emoji status";
continue;
}
if (emoji_status.get_until_date() != 0) {
LOG(ERROR) << "Receive temporary emoji status";
emoji_status.clear_until_date();
}
emoji_statuses_.push_back(emoji_status);
}
}
template <class StorerT>
void store(StorerT &storer) const {
td::store(hash_, storer);
td::store(emoji_statuses_, storer);
}
template <class ParserT>
void parse(ParserT &parser) {
td::parse(hash_, parser);
td::parse(emoji_statuses_, parser);
}
};
static const string &get_default_emoji_statuses_database_key() {
static string key = "def_emoji_statuses";
return key;
}
static const string &get_recent_emoji_statuses_database_key() {
static string key = "rec_emoji_statuses";
return key;
}
static EmojiStatuses load_emoji_statuses(const string &key) {
EmojiStatuses result;
auto log_event_string = G()->td_db()->get_binlog_pmc()->get(key);
if (!log_event_string.empty()) {
log_event_parse(result, log_event_string).ensure();
} else {
result.hash_ = -1;
}
return result;
}
static void save_emoji_statuses(const string &key, const EmojiStatuses &emoji_statuses) {
G()->td_db()->get_binlog_pmc()->set(key, log_event_store(emoji_statuses).as_slice().str());
}
class GetDefaultEmojiStatusesQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::emojiStatuses>> promise_;
public:
explicit GetDefaultEmojiStatusesQuery(Promise<td_api::object_ptr<td_api::emojiStatuses>> &&promise)
: promise_(std::move(promise)) {
}
void send(int64 hash) {
send_query(G()->net_query_creator().create(telegram_api::account_getDefaultEmojiStatuses(hash), {{"me"}}));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_getDefaultEmojiStatuses>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto emoji_statuses_ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for GetDefaultEmojiStatusesQuery: " << to_string(emoji_statuses_ptr);
if (emoji_statuses_ptr->get_id() == telegram_api::account_emojiStatusesNotModified::ID) {
if (promise_) {
promise_.set_error(Status::Error(500, "Receive wrong server response"));
}
return;
}
CHECK(emoji_statuses_ptr->get_id() == telegram_api::account_emojiStatuses::ID);
EmojiStatuses emoji_statuses(move_tl_object_as<telegram_api::account_emojiStatuses>(emoji_statuses_ptr));
save_emoji_statuses(get_default_emoji_statuses_database_key(), emoji_statuses);
if (promise_) {
promise_.set_value(emoji_statuses.get_emoji_statuses_object());
}
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class GetRecentEmojiStatusesQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::emojiStatuses>> promise_;
public:
explicit GetRecentEmojiStatusesQuery(Promise<td_api::object_ptr<td_api::emojiStatuses>> &&promise)
: promise_(std::move(promise)) {
}
void send(int64 hash) {
send_query(G()->net_query_creator().create(telegram_api::account_getRecentEmojiStatuses(hash), {{"me"}}));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_getRecentEmojiStatuses>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto emoji_statuses_ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for GetRecentEmojiStatusesQuery: " << to_string(emoji_statuses_ptr);
if (emoji_statuses_ptr->get_id() == telegram_api::account_emojiStatusesNotModified::ID) {
if (promise_) {
promise_.set_error(Status::Error(500, "Receive wrong server response"));
}
return;
}
CHECK(emoji_statuses_ptr->get_id() == telegram_api::account_emojiStatuses::ID);
EmojiStatuses emoji_statuses(move_tl_object_as<telegram_api::account_emojiStatuses>(emoji_statuses_ptr));
save_emoji_statuses(get_recent_emoji_statuses_database_key(), emoji_statuses);
if (promise_) {
promise_.set_value(emoji_statuses.get_emoji_statuses_object());
}
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class ClearRecentEmojiStatusesQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit ClearRecentEmojiStatusesQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send() {
send_query(G()->net_query_creator().create(telegram_api::account_clearRecentEmojiStatuses(), {{"me"}}));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_clearRecentEmojiStatuses>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
save_emoji_statuses(get_recent_emoji_statuses_database_key(), EmojiStatuses());
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
EmojiStatus::EmojiStatus(const td_api::object_ptr<td_api::emojiStatus> &emoji_status, int32 duration) {
if (emoji_status == nullptr) {
return;
}
custom_emoji_id_ = emoji_status->custom_emoji_id_;
if (duration != 0) {
int32 current_time = G()->unix_time();
if (duration >= std::numeric_limits<int32>::max() - current_time) {
until_date_ = std::numeric_limits<int32>::max();
} else {
until_date_ = current_time + duration;
}
}
}
EmojiStatus::EmojiStatus(tl_object_ptr<telegram_api::EmojiStatus> &&emoji_status) {
if (emoji_status == nullptr) {
return;
}
switch (emoji_status->get_id()) {
case telegram_api::emojiStatusEmpty::ID:
break;
case telegram_api::emojiStatus::ID: {
auto status = static_cast<const telegram_api::emojiStatus *>(emoji_status.get());
custom_emoji_id_ = status->document_id_;
break;
}
case telegram_api::emojiStatusUntil::ID: {
auto status = static_cast<const telegram_api::emojiStatusUntil *>(emoji_status.get());
custom_emoji_id_ = status->document_id_;
until_date_ = status->until_;
break;
}
default:
UNREACHABLE();
}
}
tl_object_ptr<telegram_api::EmojiStatus> EmojiStatus::get_input_emoji_status() const {
if (is_empty()) {
return make_tl_object<telegram_api::emojiStatusEmpty>();
}
if (until_date_ != 0) {
return make_tl_object<telegram_api::emojiStatusUntil>(custom_emoji_id_, until_date_);
}
return make_tl_object<telegram_api::emojiStatus>(custom_emoji_id_);
}
td_api::object_ptr<td_api::emojiStatus> EmojiStatus::get_emoji_status_object() const {
if (is_empty()) {
return nullptr;
}
return td_api::make_object<td_api::emojiStatus>(custom_emoji_id_);
}
int64 EmojiStatus::get_effective_custom_emoji_id(bool is_premium, int32 unix_time) const {
if (!is_premium) {
return 0;
}
if (until_date_ != 0 && until_date_ <= unix_time) {
return 0;
}
return custom_emoji_id_;
}
StringBuilder &operator<<(StringBuilder &string_builder, const EmojiStatus &emoji_status) {
if (emoji_status.is_empty()) {
return string_builder << "DefaultProfileBadge";
}
string_builder << "CustomEmoji " << emoji_status.custom_emoji_id_;
if (emoji_status.until_date_ != 0) {
string_builder << " until " << emoji_status.until_date_;
}
return string_builder;
}
void get_default_emoji_statuses(Td *td, Promise<td_api::object_ptr<td_api::emojiStatuses>> &&promise) {
auto statuses = load_emoji_statuses(get_default_emoji_statuses_database_key());
if (statuses.hash_ != -1 && promise) {
promise.set_value(statuses.get_emoji_statuses_object());
promise = Promise<td_api::object_ptr<td_api::emojiStatuses>>();
}
td->create_handler<GetDefaultEmojiStatusesQuery>(std::move(promise))->send(statuses.hash_);
}
void get_recent_emoji_statuses(Td *td, Promise<td_api::object_ptr<td_api::emojiStatuses>> &&promise) {
auto statuses = load_emoji_statuses(get_recent_emoji_statuses_database_key());
if (statuses.hash_ != -1 && promise) {
promise.set_value(statuses.get_emoji_statuses_object());
promise = Promise<td_api::object_ptr<td_api::emojiStatuses>>();
}
td->create_handler<GetRecentEmojiStatusesQuery>(std::move(promise))->send(statuses.hash_);
}
void add_recent_emoji_status(Td *td, EmojiStatus emoji_status) {
if (emoji_status.is_empty()) {
return;
}
if (td->stickers_manager_->is_default_emoji_status(emoji_status.get_custom_emoji_id())) {
LOG(INFO) << "Skip adding themed emoji status to recents";
return;
}
emoji_status.clear_until_date();
auto statuses = load_emoji_statuses(get_recent_emoji_statuses_database_key());
if (!statuses.emoji_statuses_.empty() && statuses.emoji_statuses_[0] == emoji_status) {
return;
}
statuses.hash_ = 0;
td::remove(statuses.emoji_statuses_, emoji_status);
statuses.emoji_statuses_.insert(statuses.emoji_statuses_.begin(), emoji_status);
constexpr size_t MAX_RECENT_EMOJI_STATUSES = 50; // server-side limit
if (statuses.emoji_statuses_.size() > MAX_RECENT_EMOJI_STATUSES) {
statuses.emoji_statuses_.resize(MAX_RECENT_EMOJI_STATUSES);
}
save_emoji_statuses(get_recent_emoji_statuses_database_key(), statuses);
}
void clear_recent_emoji_statuses(Td *td, Promise<Unit> &&promise) {
save_emoji_statuses(get_recent_emoji_statuses_database_key(), EmojiStatuses());
td->create_handler<ClearRecentEmojiStatusesQuery>(std::move(promise))->send();
}
} // namespace td

109
td/telegram/EmojiStatus.h Normal file
View File

@ -0,0 +1,109 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/utils/common.h"
#include "td/utils/Promise.h"
#include "td/utils/StringBuilder.h"
#include "td/utils/tl_helpers.h"
namespace td {
class Td;
class EmojiStatus {
int64 custom_emoji_id_ = 0;
int32 until_date_ = 0;
friend bool operator==(const EmojiStatus &lhs, const EmojiStatus &rhs);
friend StringBuilder &operator<<(StringBuilder &string_builder, const EmojiStatus &contact);
public:
EmojiStatus() = default;
EmojiStatus(const td_api::object_ptr<td_api::emojiStatus> &emoji_status, int32 duration);
explicit EmojiStatus(tl_object_ptr<telegram_api::EmojiStatus> &&emoji_status);
tl_object_ptr<telegram_api::EmojiStatus> get_input_emoji_status() const;
td_api::object_ptr<td_api::emojiStatus> get_emoji_status_object() const;
int64 get_effective_custom_emoji_id(bool is_premium, int32 unix_time) const;
bool is_empty() const {
return custom_emoji_id_ == 0;
}
int64 get_custom_emoji_id() const {
return custom_emoji_id_;
}
int32 get_until_date() const {
return until_date_;
}
void clear_until_date() {
until_date_ = 0;
}
template <class StorerT>
void store(StorerT &storer) const {
bool has_custom_emoji_id = custom_emoji_id_ != 0;
bool has_until_date = until_date_ != 0;
BEGIN_STORE_FLAGS();
STORE_FLAG(has_custom_emoji_id);
STORE_FLAG(has_until_date);
END_STORE_FLAGS();
if (has_custom_emoji_id) {
td::store(custom_emoji_id_, storer);
}
if (has_until_date) {
td::store(until_date_, storer);
}
}
template <class ParserT>
void parse(ParserT &parser) {
bool has_custom_emoji_id;
bool has_until_date;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_custom_emoji_id);
PARSE_FLAG(has_until_date);
END_PARSE_FLAGS();
if (has_custom_emoji_id) {
td::parse(custom_emoji_id_, parser);
}
if (has_until_date) {
td::parse(until_date_, parser);
}
}
};
inline bool operator==(const EmojiStatus &lhs, const EmojiStatus &rhs) {
return lhs.custom_emoji_id_ == rhs.custom_emoji_id_ && lhs.until_date_ == rhs.until_date_;
}
inline bool operator!=(const EmojiStatus &lhs, const EmojiStatus &rhs) {
return !(lhs == rhs);
}
StringBuilder &operator<<(StringBuilder &string_builder, const EmojiStatus &emoji_status);
void get_default_emoji_statuses(Td *td, Promise<td_api::object_ptr<td_api::emojiStatuses>> &&promise);
void get_recent_emoji_statuses(Td *td, Promise<td_api::object_ptr<td_api::emojiStatuses>> &&promise);
void add_recent_emoji_status(Td *td, EmojiStatus emoji_status);
void clear_recent_emoji_statuses(Td *td, Promise<Unit> &&promise);
} // namespace td

View File

@ -175,7 +175,7 @@ class RequestSimpleWebViewQuery final : public Td::ResultHandler {
}
void send(tl_object_ptr<telegram_api::InputUser> &&input_user, const string &url,
const td_api::object_ptr<td_api::themeParameters> &theme) {
const td_api::object_ptr<td_api::themeParameters> &theme, string &&platform) {
tl_object_ptr<telegram_api::dataJSON> theme_parameters;
int32 flags = 0;
if (theme != nullptr) {
@ -184,8 +184,8 @@ class RequestSimpleWebViewQuery final : public Td::ResultHandler {
theme_parameters = make_tl_object<telegram_api::dataJSON>(string());
theme_parameters->data_ = ThemeManager::get_theme_parameters_json_string(theme, false);
}
send_query(G()->net_query_creator().create(
telegram_api::messages_requestSimpleWebView(flags, std::move(input_user), url, std::move(theme_parameters))));
send_query(G()->net_query_creator().create(telegram_api::messages_requestSimpleWebView(
flags, std::move(input_user), url, std::move(theme_parameters), platform)));
}
void on_result(BufferSlice packet) final {
@ -347,7 +347,7 @@ Result<tl_object_ptr<telegram_api::InputBotInlineMessage>> InlineQueriesManager:
tl_object_ptr<td_api::InputMessageContent> &&input_message_content,
tl_object_ptr<td_api::ReplyMarkup> &&reply_markup_ptr, int32 allowed_media_content_id) const {
if (input_message_content == nullptr) {
return Status::Error(400, "Inline message can't be empty");
return Status::Error(400, "Inline message must be non-empty");
}
TRY_RESULT(reply_markup, get_reply_markup(std::move(reply_markup_ptr), true, true, false, true));
auto input_reply_markup = get_input_reply_markup(td_->contacts_manager_.get(), reply_markup);
@ -500,11 +500,12 @@ void InlineQueriesManager::answer_inline_query(
void InlineQueriesManager::get_simple_web_view_url(UserId bot_user_id, string &&url,
const td_api::object_ptr<td_api::themeParameters> &theme,
Promise<string> &&promise) {
string &&platform, Promise<string> &&promise) {
TRY_RESULT_PROMISE(promise, input_user, td_->contacts_manager_->get_input_user(bot_user_id));
TRY_RESULT_PROMISE(promise, bot_data, td_->contacts_manager_->get_bot_data(bot_user_id));
td_->create_handler<RequestSimpleWebViewQuery>(std::move(promise))->send(std::move(input_user), url, theme);
td_->create_handler<RequestSimpleWebViewQuery>(std::move(promise))
->send(std::move(input_user), url, theme, std::move(platform));
}
void InlineQueriesManager::send_web_view_data(UserId bot_user_id, string &&button_text, string &&data,

View File

@ -48,7 +48,8 @@ class InlineQueriesManager final : public Actor {
Promise<Unit> &&promise) const;
void get_simple_web_view_url(UserId bot_user_id, string &&url,
const td_api::object_ptr<td_api::themeParameters> &theme, Promise<string> &&promise);
const td_api::object_ptr<td_api::themeParameters> &theme, string &&platform,
Promise<string> &&promise);
void send_web_view_data(UserId bot_user_id, string &&button_text, string &&data, Promise<Unit> &&promise) const;

View File

@ -10,6 +10,7 @@
#include "td/telegram/ChatId.h"
#include "td/telegram/UserId.h"
#include "td/utils/algorithm.h"
#include "td/utils/logging.h"
namespace td {
@ -70,6 +71,10 @@ vector<InputDialogId> InputDialogId::get_input_dialog_ids(
return result;
}
vector<DialogId> InputDialogId::get_dialog_ids(const vector<InputDialogId> &input_dialog_ids) {
return transform(input_dialog_ids, [](InputDialogId input_dialog_id) { return input_dialog_id.get_dialog_id(); });
}
vector<telegram_api::object_ptr<telegram_api::InputDialogPeer>> InputDialogId::get_input_dialog_peers(
const vector<InputDialogId> &input_dialog_ids) {
vector<telegram_api::object_ptr<telegram_api::InputDialogPeer>> result;
@ -143,4 +148,10 @@ bool InputDialogId::contains(const vector<InputDialogId> &input_dialog_ids, Dial
return false;
}
bool InputDialogId::remove(vector<InputDialogId> &input_dialog_ids, DialogId dialog_id) {
return td::remove_if(input_dialog_ids, [dialog_id](InputDialogId input_dialog_id) {
return input_dialog_id.get_dialog_id() == dialog_id;
});
}
} // namespace td

View File

@ -30,6 +30,8 @@ class InputDialogId {
static vector<InputDialogId> get_input_dialog_ids(const vector<tl_object_ptr<telegram_api::InputPeer>> &input_peers,
FlatHashSet<DialogId, DialogIdHash> *added_dialog_ids = nullptr);
static vector<DialogId> get_dialog_ids(const vector<InputDialogId> &input_dialog_ids);
static vector<telegram_api::object_ptr<telegram_api::InputDialogPeer>> get_input_dialog_peers(
const vector<InputDialogId> &input_dialog_ids);
@ -40,6 +42,8 @@ class InputDialogId {
static bool contains(const vector<InputDialogId> &input_dialog_ids, DialogId dialog_id);
static bool remove(vector<InputDialogId> &input_dialog_ids, DialogId dialog_id);
bool operator==(const InputDialogId &other) const {
return dialog_id == other.dialog_id && access_hash == other.access_hash;
}

View File

@ -217,6 +217,15 @@ int32 get_json_value_int(telegram_api::object_ptr<telegram_api::JSONValue> &&jso
return 0;
}
int64 get_json_value_long(telegram_api::object_ptr<telegram_api::JSONValue> &&json_value, Slice name) {
CHECK(json_value != nullptr);
if (json_value->get_id() == telegram_api::jsonString::ID) {
return to_integer<int64>(static_cast<const telegram_api::jsonString *>(json_value.get())->value_);
}
LOG(ERROR) << "Expected Long as " << name << ", but found " << to_string(json_value);
return 0;
}
double get_json_value_double(telegram_api::object_ptr<telegram_api::JSONValue> &&json_value, Slice name) {
CHECK(json_value != nullptr);
if (json_value->get_id() == telegram_api::jsonNumber::ID) {

View File

@ -30,6 +30,8 @@ bool get_json_value_bool(telegram_api::object_ptr<telegram_api::JSONValue> &&jso
int32 get_json_value_int(telegram_api::object_ptr<telegram_api::JSONValue> &&json_value, Slice name);
int64 get_json_value_long(telegram_api::object_ptr<telegram_api::JSONValue> &&json_value, Slice name);
double get_json_value_double(telegram_api::object_ptr<telegram_api::JSONValue> &&json_value, Slice name);
string get_json_value_string(telegram_api::object_ptr<telegram_api::JSONValue> &&json_value, Slice name);

View File

@ -7,7 +7,6 @@
#include "td/telegram/LanguagePackManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/logevent/LogEvent.h"
#include "td/telegram/misc.h"
#include "td/telegram/net/NetQueryDispatcher.h"
#include "td/telegram/Td.h"

View File

@ -337,6 +337,18 @@ class LinkManager::InternalLinkGame final : public InternalLink {
}
};
class LinkManager::InternalLinkInstantView final : public InternalLink {
string url_;
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypeInstantView>(url_);
}
public:
explicit InternalLinkInstantView(string url) : url_(std::move(url)) {
}
};
class LinkManager::InternalLinkInvoice final : public InternalLink {
string invoice_name_;
@ -876,8 +888,7 @@ LinkManager::LinkInfo LinkManager::get_link_info(Slice link) {
return result;
}
result.is_internal_ = true;
result.is_tg_ = true;
result.type_ = LinkType::Tg;
result.query_ = link.str();
return result;
} else {
@ -885,9 +896,27 @@ LinkManager::LinkInfo LinkManager::get_link_info(Slice link) {
return result;
}
auto host = url_decode(http_url.host_, false);
to_lower_inplace(host);
if (ends_with(host, ".t.me") && host.size() >= 9 && host.find('.') == host.size() - 5) {
Slice subdomain(&host[0], host.size() - 5);
if (is_valid_username(subdomain) && subdomain != "addemoji" && subdomain != "addstickers" &&
subdomain != "addtheme" && subdomain != "auth" && subdomain != "confirmphone" && subdomain != "invoice" &&
subdomain != "joinchat" && subdomain != "login" && subdomain != "proxy" && subdomain != "setlanguage" &&
subdomain != "share" && subdomain != "socks") {
result.type_ = LinkType::TMe;
result.query_ = PSTRING() << '/' << subdomain << http_url.query_;
return result;
}
}
if (begins_with(host, "www.")) {
host = host.substr(4);
}
string cur_t_me_url;
vector<Slice> t_me_urls{Slice("t.me"), Slice("telegram.me"), Slice("telegram.dog")};
if (Scheduler::context() != nullptr) { // for tests only
string cur_t_me_url = G()->get_option_string("t_me_url");
cur_t_me_url = G()->get_option_string("t_me_url");
if (tolower_begins_with(cur_t_me_url, "http://") || tolower_begins_with(cur_t_me_url, "https://")) {
Slice t_me_url = cur_t_me_url;
t_me_url = t_me_url.substr(t_me_url[4] == 's' ? 8 : 7);
@ -897,16 +926,9 @@ LinkManager::LinkInfo LinkManager::get_link_info(Slice link) {
}
}
auto host = url_decode(http_url.host_, false);
to_lower_inplace(host);
if (begins_with(host, "www.")) {
host = host.substr(4);
}
for (auto t_me_url : t_me_urls) {
if (host == t_me_url) {
result.is_internal_ = true;
result.is_tg_ = false;
result.type_ = LinkType::TMe;
Slice query = http_url.query_;
while (true) {
@ -924,24 +946,39 @@ LinkManager::LinkInfo LinkManager::get_link_info(Slice link) {
return result;
}
}
if (http_url.query_.size() > 1) {
for (auto telegraph_url : {Slice("telegra.ph"), Slice("te.legra.ph"), Slice("graph.org")}) {
if (host == telegraph_url) {
result.type_ = LinkType::Telegraph;
result.query_ = std::move(http_url.query_);
return result;
}
}
}
}
return result;
}
bool LinkManager::is_internal_link(Slice link) {
auto info = get_link_info(link);
return info.is_internal_;
return info.type_ != LinkType::External;
}
unique_ptr<LinkManager::InternalLink> LinkManager::parse_internal_link(Slice link, bool is_trusted) {
auto info = get_link_info(link);
if (!info.is_internal_) {
return nullptr;
}
if (info.is_tg_) {
return parse_tg_link_query(info.query_, is_trusted);
} else {
return parse_t_me_link_query(info.query_, is_trusted);
switch (info.type_) {
case LinkType::External:
return nullptr;
case LinkType::Tg:
return parse_tg_link_query(info.query_, is_trusted);
case LinkType::TMe:
return parse_t_me_link_query(info.query_, is_trusted);
case LinkType::Telegraph:
return td::make_unique<InternalLinkInstantView>(PSTRING() << "https://telegra.ph" << info.query_);
default:
UNREACHABLE();
return nullptr;
}
}
@ -1324,6 +1361,12 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_t_me_link_query(Slice q
// /share/url?url=<url>&text=<text>
return get_internal_link_message_draft(get_arg("url"), get_arg("text"));
}
} else if (path[0] == "iv") {
if (path.size() == 1 && has_arg("url")) {
// /iv?url=<url>&rhash=<rhash>
return td::make_unique<InternalLinkInstantView>(PSTRING()
<< "https://t.me/iv" << copy_arg("url") << copy_arg("rhash"));
}
} else if (is_valid_username(path[0])) {
if (path.size() >= 2 && to_integer<int64>(path[1]) > 0) {
// /<username>/12345?single&thread=<thread_id>&comment=<message_id>&t=<media_timestamp>
@ -1554,11 +1597,11 @@ void LinkManager::get_link_login_url(const string &url, bool allow_write_access,
string LinkManager::get_dialog_invite_link_hash(Slice invite_link) {
auto link_info = get_link_info(invite_link);
if (!link_info.is_internal_) {
if (link_info.type_ != LinkType::Tg && link_info.type_ != LinkType::TMe) {
return string();
}
const auto url_query = parse_url_query(link_info.query_);
return get_url_query_hash(link_info.is_tg_, url_query);
return get_url_query_hash(link_info.type_ == LinkType::Tg, url_query);
}
string LinkManager::get_dialog_invite_link(Slice hash, bool is_internal) {
@ -1661,7 +1704,7 @@ Result<MessageLinkInfo> LinkManager::get_message_link_info(Slice url) {
return Status::Error("URL must be non-empty");
}
auto link_info = get_link_info(url);
if (!link_info.is_internal_) {
if (link_info.type_ != LinkType::Tg && link_info.type_ != LinkType::TMe) {
return Status::Error("Invalid message link URL");
}
url = link_info.query_;
@ -1673,7 +1716,7 @@ Result<MessageLinkInfo> LinkManager::get_message_link_info(Slice url) {
Slice media_timestamp_slice;
bool is_single = false;
bool for_comment = false;
if (link_info.is_tg_) {
if (link_info.type_ == LinkType::Tg) {
// resolve?domain=username&post=12345&single&t=123&comment=12&thread=21
// privatepost?channel=123456789&post=12345&single&t=123&comment=12&thread=21

View File

@ -101,6 +101,7 @@ class LinkManager final : public Actor {
class InternalLinkDialogInvite;
class InternalLinkFilterSettings;
class InternalLinkGame;
class InternalLinkInstantView;
class InternalLinkInvoice;
class InternalLinkLanguage;
class InternalLinkLanguageSettings;
@ -122,9 +123,10 @@ class LinkManager final : public Actor {
class InternalLinkUserPhoneNumber;
class InternalLinkVoiceChat;
enum class LinkType : int32 { External, TMe, Tg, Telegraph };
struct LinkInfo {
bool is_internal_ = false;
bool is_tg_ = false;
LinkType type_ = LinkType::External;
string query_;
};
// returns information about the link

View File

@ -2080,14 +2080,14 @@ Result<InputMessageContent> get_input_message_content(
auto input_message = static_cast<td_api::inputMessageDocument *>(input_message_content.get());
auto file_type = input_message->disable_content_type_detection_ ? 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);
td->file_manager_->get_input_file_id(file_type, input_message->document_, dialog_id, false, is_secret);
input_thumbnail = std::move(input_message->thumbnail_);
break;
}
case td_api::inputMessagePhoto::ID: {
auto input_message = static_cast<td_api::inputMessagePhoto *>(input_message_content.get());
r_file_id =
td->file_manager_->get_input_file_id(FileType::Photo, input_message->photo_, dialog_id, false, is_secret);
r_file_id = td->file_manager_->get_input_file_id(FileType::Photo, input_message->photo_, dialog_id, false,
is_secret, false, false, true);
input_thumbnail = std::move(input_message->thumbnail_);
if (!input_message->added_sticker_file_ids_.empty()) {
sticker_file_ids = td->stickers_manager_->get_attached_sticker_file_ids(input_message->added_sticker_file_ids_);
@ -2794,7 +2794,7 @@ bool can_forward_message_content(const MessageContent *content) {
auto content_type = content->get_type();
if (content_type == MessageContentType::Text) {
auto *text = static_cast<const MessageText *>(content);
return !is_empty_string(text->text.text); // text can't be empty in the new message
return !is_empty_string(text->text.text); // text must be non-empty in the new message
}
if (content_type == MessageContentType::Poll) {
auto *poll = static_cast<const MessagePoll *>(content);
@ -4686,7 +4686,7 @@ unique_ptr<MessageContent> dup_message_content(Td *td, DialogId dialog_id, const
if (type == MessageContentDupType::Copy || type == MessageContentDupType::ServerCopy) {
remove_unallowed_entities(td, result->text, dialog_id);
}
return result;
return std::move(result);
}
case MessageContentType::Venue:
return make_unique<MessageVenue>(*static_cast<const MessageVenue *>(content));
@ -5937,15 +5937,37 @@ void add_message_content_dependencies(Dependencies &dependencies, const MessageC
void on_sent_message_content(Td *td, const MessageContent *content) {
switch (content->get_type()) {
case MessageContentType::Animation:
return td->animations_manager_->add_saved_animation_by_id(get_message_content_any_file_id(content));
return td->animations_manager_->add_saved_animation_by_id(get_message_content_upload_file_id(content));
case MessageContentType::Sticker:
return td->stickers_manager_->add_recent_sticker_by_id(false, get_message_content_any_file_id(content));
return td->stickers_manager_->add_recent_sticker_by_id(false, get_message_content_upload_file_id(content));
default:
// nothing to do
return;
}
}
void move_message_content_sticker_set_to_top(Td *td, const MessageContent *content) {
CHECK(content != nullptr);
if (content->get_type() == MessageContentType::Sticker) {
td->stickers_manager_->move_sticker_set_to_top_by_sticker_id(get_message_content_upload_file_id(content));
return;
}
auto text = get_message_content_text(content);
if (text == nullptr) {
return;
}
vector<int64> custom_emoji_ids;
for (auto &entity : text->entities) {
if (entity.type == MessageEntity::Type::CustomEmoji) {
custom_emoji_ids.push_back(entity.document_id);
}
}
if (!custom_emoji_ids.empty()) {
td->stickers_manager_->move_sticker_set_to_top_by_custom_emoji_ids(custom_emoji_ids);
}
}
bool is_unsent_animated_emoji_click(Td *td, DialogId dialog_id, const DialogAction &action) {
auto emoji = action.get_watching_animations_emoji();
if (emoji.empty()) {

View File

@ -246,6 +246,8 @@ void add_message_content_dependencies(Dependencies &dependencies, const MessageC
void on_sent_message_content(Td *td, const MessageContent *content);
void move_message_content_sticker_set_to_top(Td *td, const MessageContent *content);
bool is_unsent_animated_emoji_click(Td *td, DialogId dialog_id, const DialogAction &action);
void init_stickers_manager(Td *td);

View File

@ -12,6 +12,7 @@
#include "td/telegram/Global.h"
#include "td/telegram/MessageSender.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/misc.h"
#include "td/telegram/OptionManager.h"
#include "td/telegram/ServerMessageId.h"
#include "td/telegram/StickersManager.h"
@ -21,16 +22,103 @@
#include "td/actor/actor.h"
#include "td/utils/algorithm.h"
#include "td/utils/as.h"
#include "td/utils/base64.h"
#include "td/utils/buffer.h"
#include "td/utils/crypto.h"
#include "td/utils/emoji.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/logging.h"
#include "td/utils/Status.h"
#include "td/utils/utf8.h"
#include <algorithm>
#include <utility>
namespace td {
static size_t get_max_reaction_count() {
bool is_premium = G()->get_option_boolean("is_premium");
auto option_key = is_premium ? Slice("reactions_user_max_premium") : Slice("reactions_user_max_default");
return static_cast<size_t>(
max(static_cast<int32>(1), static_cast<int32>(G()->get_option_integer(option_key, is_premium ? 3 : 1))));
}
static int64 get_custom_emoji_id(const string &reaction) {
auto r_decoded = base64_decode(Slice(&reaction[1], reaction.size() - 1));
CHECK(r_decoded.is_ok());
CHECK(r_decoded.ok().size() == 8);
return as<int64>(r_decoded.ok().c_str());
}
static string get_custom_emoji_string(int64 custom_emoji_id) {
char s[8];
as<int64>(&s) = custom_emoji_id;
return PSTRING() << '#' << base64_encode(Slice(s, 8));
}
telegram_api::object_ptr<telegram_api::Reaction> get_input_reaction(const string &reaction) {
if (reaction.empty()) {
return telegram_api::make_object<telegram_api::reactionEmpty>();
}
if (is_custom_reaction(reaction)) {
return telegram_api::make_object<telegram_api::reactionCustomEmoji>(get_custom_emoji_id(reaction));
}
return telegram_api::make_object<telegram_api::reactionEmoji>(reaction);
}
string get_message_reaction_string(const telegram_api::object_ptr<telegram_api::Reaction> &reaction) {
if (reaction == nullptr) {
return string();
}
switch (reaction->get_id()) {
case telegram_api::reactionEmpty::ID:
return string();
case telegram_api::reactionEmoji::ID: {
const string &emoji = static_cast<const telegram_api::reactionEmoji *>(reaction.get())->emoticon_;
if (is_custom_reaction(emoji)) {
return string();
}
return emoji;
}
case telegram_api::reactionCustomEmoji::ID:
return get_custom_emoji_string(
static_cast<const telegram_api::reactionCustomEmoji *>(reaction.get())->document_id_);
default:
UNREACHABLE();
return string();
}
}
td_api::object_ptr<td_api::ReactionType> get_reaction_type_object(const string &reaction) {
CHECK(!reaction.empty());
if (is_custom_reaction(reaction)) {
return td_api::make_object<td_api::reactionTypeCustomEmoji>(get_custom_emoji_id(reaction));
}
return td_api::make_object<td_api::reactionTypeEmoji>(reaction);
}
string get_message_reaction_string(const td_api::object_ptr<td_api::ReactionType> &type) {
if (type == nullptr) {
return string();
}
switch (type->get_id()) {
case td_api::reactionTypeEmoji::ID: {
const string &emoji = static_cast<const td_api::reactionTypeEmoji *>(type.get())->emoji_;
if (!check_utf8(emoji) || is_custom_reaction(emoji)) {
return string();
}
return emoji;
}
case td_api::reactionTypeCustomEmoji::ID:
return get_custom_emoji_string(
static_cast<const td_api::reactionTypeCustomEmoji *>(type.get())->custom_emoji_id_);
default:
UNREACHABLE();
return string();
}
}
class GetMessagesReactionsQuery final : public Td::ResultHandler {
DialogId dialog_id_;
vector<MessageId> message_ids_;
@ -91,7 +179,7 @@ class SendReactionQuery final : public Td::ResultHandler {
explicit SendReactionQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(FullMessageId full_message_id, string reaction, bool is_big) {
void send(FullMessageId full_message_id, vector<string> reactions, bool is_big, bool add_to_recent) {
dialog_id_ = full_message_id.get_dialog_id();
auto input_peer = td_->messages_manager_->get_input_peer(dialog_id_, AccessRights::Read);
@ -100,17 +188,22 @@ class SendReactionQuery final : public Td::ResultHandler {
}
int32 flags = 0;
if (!reaction.empty()) {
if (!reactions.empty()) {
flags |= telegram_api::messages_sendReaction::REACTION_MASK;
if (is_big) {
flags |= telegram_api::messages_sendReaction::BIG_MASK;
}
if (add_to_recent) {
flags |= telegram_api::messages_sendReaction::ADD_TO_RECENT_MASK;
}
}
send_query(G()->net_query_creator().create(
telegram_api::messages_sendReaction(flags, false /*ignored*/, std::move(input_peer),
full_message_id.get_message_id().get_server_message_id().get(), reaction),
telegram_api::messages_sendReaction(flags, false /*ignored*/, false /*ignored*/, std::move(input_peer),
full_message_id.get_message_id().get_server_message_id().get(),
transform(reactions, get_input_reaction)),
{{dialog_id_}, {full_message_id}}));
}
@ -163,8 +256,9 @@ class GetMessageReactionsListQuery final : public Td::ResultHandler {
}
send_query(G()->net_query_creator().create(
telegram_api::messages_getMessageReactionsList(
flags, std::move(input_peer), message_id_.get_server_message_id().get(), reaction_, offset_, limit),
telegram_api::messages_getMessageReactionsList(flags, std::move(input_peer),
message_id_.get_server_message_id().get(),
get_input_reaction(reaction_), offset_, limit),
{{full_message_id}}));
}
@ -191,19 +285,20 @@ class GetMessageReactionsListQuery final : public Td::ResultHandler {
FlatHashMap<string, vector<DialogId>> recent_reactions;
for (const auto &reaction : ptr->reactions_) {
DialogId dialog_id(reaction->peer_id_);
if (!dialog_id.is_valid() ||
(reaction_.empty() ? reaction->reaction_.empty() : reaction_ != reaction->reaction_)) {
auto reaction_str = get_message_reaction_string(reaction->reaction_);
if (!dialog_id.is_valid() || (reaction_.empty() ? reaction_str.empty() : reaction_ != reaction_str)) {
LOG(ERROR) << "Receive unexpected " << to_string(reaction);
continue;
}
if (offset_.empty()) {
recent_reactions[reaction->reaction_].push_back(dialog_id);
recent_reactions[reaction_str].push_back(dialog_id);
}
auto message_sender = get_min_message_sender_object(td_, dialog_id, "GetMessageReactionsListQuery");
if (message_sender != nullptr) {
reactions.push_back(td_api::make_object<td_api::addedReaction>(reaction->reaction_, std::move(message_sender)));
reactions.push_back(td_api::make_object<td_api::addedReaction>(get_reaction_type_object(reaction_str),
std::move(message_sender)));
}
}
@ -228,7 +323,8 @@ class SetDefaultReactionQuery final : public Td::ResultHandler {
public:
void send(const string &reaction) {
reaction_ = reaction;
send_query(G()->net_query_creator().create(telegram_api::messages_setDefaultReaction(reaction)));
send_query(
G()->net_query_creator().create(telegram_api::messages_setDefaultReaction(get_input_reaction(reaction))));
}
void on_result(BufferSlice packet) final {
@ -242,8 +338,6 @@ class SetDefaultReactionQuery final : public Td::ResultHandler {
}
auto default_reaction = td_->option_manager_->get_option_string("default_reaction", "-");
LOG(INFO) << "Successfully set reaction " << reaction_ << " as default, current default is " << default_reaction;
if (default_reaction != reaction_) {
send_set_default_reaction_query(td_);
} else {
@ -256,9 +350,47 @@ class SetDefaultReactionQuery final : public Td::ResultHandler {
return;
}
LOG(INFO) << "Failed to set default reaction: " << status;
LOG(INFO) << "Receive error for SetDefaultReactionQuery: " << status;
td_->option_manager_->set_option_empty("default_reaction_needs_sync");
send_closure(G()->config_manager(), &ConfigManager::reget_app_config, Promise<Unit>());
send_closure(G()->config_manager(), &ConfigManager::request_config, false);
}
};
class ReportReactionQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
DialogId dialog_id_;
public:
explicit ReportReactionQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(DialogId dialog_id, MessageId message_id, DialogId chooser_dialog_id) {
dialog_id_ = dialog_id;
auto input_peer = td_->messages_manager_->get_input_peer(dialog_id_, AccessRights::Read);
CHECK(input_peer != nullptr);
auto chooser_input_peer = td_->messages_manager_->get_input_peer(chooser_dialog_id, AccessRights::Know);
if (chooser_input_peer == nullptr) {
return promise_.set_error(Status::Error(400, "Reaction sender is not accessible"));
}
send_query(G()->net_query_creator().create(telegram_api::messages_reportReaction(
std::move(input_peer), message_id.get_server_message_id().get(), std::move(chooser_input_peer))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_reportReaction>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
promise_.set_value(Unit());
}
void on_error(Status status) final {
td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "ReportReactionQuery");
promise_.set_error(std::move(status));
}
};
@ -290,7 +422,7 @@ void MessageReaction::update_recent_chooser_dialog_ids(const MessageReaction &ol
recent_chooser_min_channels_ = old_reaction.recent_chooser_min_channels_;
}
void MessageReaction::set_is_chosen(bool is_chosen, DialogId chooser_dialog_id, bool can_get_added_reactions) {
void MessageReaction::set_is_chosen(bool is_chosen, DialogId chooser_dialog_id, bool have_recent_choosers) {
if (is_chosen_ == is_chosen) {
return;
}
@ -299,7 +431,7 @@ void MessageReaction::set_is_chosen(bool is_chosen, DialogId chooser_dialog_id,
if (chooser_dialog_id.is_valid()) {
choose_count_ += is_chosen_ ? 1 : -1;
if (can_get_added_reactions) {
if (have_recent_choosers) {
remove_recent_chooser_dialog_id(chooser_dialog_id);
if (is_chosen_) {
add_recent_chooser_dialog_id(chooser_dialog_id);
@ -308,20 +440,38 @@ void MessageReaction::set_is_chosen(bool is_chosen, DialogId chooser_dialog_id,
}
}
td_api::object_ptr<td_api::messageReaction> MessageReaction::get_message_reaction_object(Td *td) const {
td_api::object_ptr<td_api::messageReaction> MessageReaction::get_message_reaction_object(Td *td, UserId my_user_id,
UserId peer_user_id) const {
CHECK(!is_empty());
vector<td_api::object_ptr<td_api::MessageSender>> recent_choosers;
for (auto dialog_id : recent_chooser_dialog_ids_) {
auto recent_chooser = get_min_message_sender_object(td, dialog_id, "get_message_reaction_object");
if (recent_chooser != nullptr) {
recent_choosers.push_back(std::move(recent_chooser));
if (recent_choosers.size() == MAX_RECENT_CHOOSERS) {
break;
if (my_user_id.is_valid()) {
CHECK(peer_user_id.is_valid());
if (is_chosen()) {
auto recent_chooser = get_min_message_sender_object(td, DialogId(my_user_id), "get_message_reaction_object");
if (recent_chooser != nullptr) {
recent_choosers.push_back(std::move(recent_chooser));
}
}
if (choose_count_ >= (is_chosen() ? 2 : 1)) {
auto recent_chooser = get_min_message_sender_object(td, DialogId(peer_user_id), "get_message_reaction_object");
if (recent_chooser != nullptr) {
recent_choosers.push_back(std::move(recent_chooser));
}
}
} else {
for (auto dialog_id : recent_chooser_dialog_ids_) {
auto recent_chooser = get_min_message_sender_object(td, dialog_id, "get_message_reaction_object");
if (recent_chooser != nullptr) {
recent_choosers.push_back(std::move(recent_chooser));
if (recent_choosers.size() == MAX_RECENT_CHOOSERS) {
break;
}
}
}
}
return td_api::make_object<td_api::messageReaction>(reaction_, choose_count_, is_chosen_, std::move(recent_choosers));
return td_api::make_object<td_api::messageReaction>(get_reaction_type_object(reaction_), choose_count_, is_chosen_,
std::move(recent_choosers));
}
bool operator==(const MessageReaction &lhs, const MessageReaction &rhs) {
@ -342,7 +492,8 @@ td_api::object_ptr<td_api::unreadReaction> UnreadMessageReaction::get_unread_rea
if (sender_id == nullptr) {
return nullptr;
}
return td_api::make_object<td_api::unreadReaction>(reaction_, std::move(sender_id), is_big_);
return td_api::make_object<td_api::unreadReaction>(get_reaction_type_object(reaction_), std::move(sender_id),
is_big_);
}
bool operator==(const UnreadMessageReaction &lhs, const UnreadMessageReaction &rhs) {
@ -365,31 +516,33 @@ unique_ptr<MessageReactions> MessageReactions::get_message_reactions(
result->is_min_ = reactions->min_;
FlatHashSet<string> reaction_strings;
FlatHashSet<DialogId, DialogIdHash> recent_choosers;
vector<std::pair<int32, string>> chosen_reaction_order;
for (auto &reaction_count : reactions->results_) {
auto reaction_str = get_message_reaction_string(reaction_count->reaction_);
if (reaction_count->count_ <= 0 || reaction_count->count_ >= MessageReaction::MAX_CHOOSE_COUNT ||
reaction_count->reaction_.empty()) {
LOG(ERROR) << "Receive reaction " << reaction_count->reaction_ << " with invalid count "
<< reaction_count->count_;
reaction_str.empty()) {
LOG(ERROR) << "Receive reaction " << reaction_str << " with invalid count " << reaction_count->count_;
continue;
}
if (!reaction_strings.insert(reaction_count->reaction_).second) {
LOG(ERROR) << "Receive duplicate reaction " << reaction_count->reaction_;
if (!reaction_strings.insert(reaction_str).second) {
LOG(ERROR) << "Receive duplicate reaction " << reaction_str;
continue;
}
FlatHashSet<DialogId, DialogIdHash> recent_choosers;
vector<DialogId> recent_chooser_dialog_ids;
vector<std::pair<ChannelId, MinChannel>> recent_chooser_min_channels;
for (auto &peer_reaction : reactions->recent_reactions_) {
if (peer_reaction->reaction_ == reaction_count->reaction_) {
auto peer_reaction_str = get_message_reaction_string(peer_reaction->reaction_);
if (peer_reaction_str == reaction_str) {
DialogId dialog_id(peer_reaction->peer_id_);
if (!dialog_id.is_valid()) {
LOG(ERROR) << "Receive invalid " << dialog_id << " as a recent chooser";
LOG(ERROR) << "Receive invalid " << dialog_id << " as a recent chooser for reaction " << reaction_str;
continue;
}
if (!recent_choosers.insert(dialog_id).second) {
LOG(ERROR) << "Receive duplicate " << dialog_id << " as a recent chooser";
LOG(ERROR) << "Receive duplicate " << dialog_id << " as a recent chooser for reaction " << reaction_str;
continue;
}
if (!td->messages_manager_->have_dialog_info(dialog_id)) {
@ -416,7 +569,7 @@ unique_ptr<MessageReactions> MessageReactions::get_message_reactions(
recent_chooser_dialog_ids.push_back(dialog_id);
if (peer_reaction->unread_) {
result->unread_reactions_.emplace_back(std::move(peer_reaction->reaction_), dialog_id, peer_reaction->big_);
result->unread_reactions_.emplace_back(std::move(peer_reaction_str), dialog_id, peer_reaction->big_);
}
if (recent_chooser_dialog_ids.size() == MessageReaction::MAX_RECENT_CHOOSERS) {
break;
@ -424,9 +577,17 @@ unique_ptr<MessageReactions> MessageReactions::get_message_reactions(
}
}
result->reactions_.emplace_back(std::move(reaction_count->reaction_), reaction_count->count_,
reaction_count->chosen_, std::move(recent_chooser_dialog_ids),
std::move(recent_chooser_min_channels));
bool is_chosen = (reaction_count->flags_ & telegram_api::reactionCount::CHOSEN_ORDER_MASK) != 0;
if (is_chosen) {
chosen_reaction_order.emplace_back(reaction_count->chosen_order_, reaction_str);
}
result->reactions_.push_back({std::move(reaction_str), reaction_count->count_, is_chosen,
std::move(recent_chooser_dialog_ids), std::move(recent_chooser_min_channels)});
}
if (chosen_reaction_order.size() > 1) {
std::sort(chosen_reaction_order.begin(), chosen_reaction_order.end());
result->chosen_reaction_order_ =
transform(chosen_reaction_order, [](const std::pair<int32, string> &order) { return order.second; });
}
return result;
}
@ -462,6 +623,7 @@ void MessageReactions::update_from(const MessageReactions &old_reactions) {
}
}
unread_reactions_ = old_reactions.unread_reactions_;
chosen_reaction_order_ = old_reactions.chosen_reaction_order_;
}
for (const auto &old_reaction : old_reactions.reactions_) {
if (old_reaction.is_chosen() &&
@ -474,6 +636,82 @@ void MessageReactions::update_from(const MessageReactions &old_reactions) {
}
}
bool MessageReactions::add_reaction(const string &reaction, bool is_big, DialogId chooser_dialog_id,
bool have_recent_choosers) {
vector<string> new_chosen_reaction_order = get_chosen_reactions();
auto added_reaction = get_reaction(reaction);
if (added_reaction == nullptr) {
vector<DialogId> recent_chooser_dialog_ids;
if (have_recent_choosers) {
recent_chooser_dialog_ids.push_back(chooser_dialog_id);
}
reactions_.push_back({reaction, 1, true, std::move(recent_chooser_dialog_ids), Auto()});
new_chosen_reaction_order.emplace_back(reaction);
} else if (!added_reaction->is_chosen()) {
added_reaction->set_is_chosen(true, chooser_dialog_id, have_recent_choosers);
new_chosen_reaction_order.emplace_back(reaction);
} else if (!is_big) {
return false;
}
auto max_reaction_count = get_max_reaction_count();
while (new_chosen_reaction_order.size() > max_reaction_count) {
auto index = new_chosen_reaction_order[0] == reaction ? 1 : 0;
CHECK(static_cast<size_t>(index) < new_chosen_reaction_order.size());
bool is_removed = do_remove_reaction(new_chosen_reaction_order[index], chooser_dialog_id, have_recent_choosers);
CHECK(is_removed);
new_chosen_reaction_order.erase(new_chosen_reaction_order.begin() + index);
}
if (new_chosen_reaction_order.size() == 1) {
new_chosen_reaction_order.clear();
}
chosen_reaction_order_ = std::move(new_chosen_reaction_order);
return true;
}
bool MessageReactions::remove_reaction(const string &reaction, DialogId chooser_dialog_id, bool have_recent_choosers) {
if (do_remove_reaction(reaction, chooser_dialog_id, have_recent_choosers)) {
if (!chosen_reaction_order_.empty()) {
bool is_removed = td::remove(chosen_reaction_order_, reaction);
CHECK(is_removed);
auto max_reaction_count = get_max_reaction_count();
while (chosen_reaction_order_.size() > max_reaction_count) {
is_removed = do_remove_reaction(chosen_reaction_order_[0], chooser_dialog_id, have_recent_choosers);
CHECK(is_removed);
chosen_reaction_order_.erase(chosen_reaction_order_.begin());
}
if (chosen_reaction_order_.size() <= 1) {
reset_to_empty(chosen_reaction_order_);
}
}
return true;
}
return false;
}
bool MessageReactions::do_remove_reaction(const string &reaction, DialogId chooser_dialog_id,
bool have_recent_choosers) {
for (auto it = reactions_.begin(); it != reactions_.end(); ++it) {
auto &message_reaction = *it;
if (message_reaction.get_reaction() == reaction) {
if (message_reaction.is_chosen()) {
message_reaction.set_is_chosen(false, chooser_dialog_id, have_recent_choosers);
if (message_reaction.is_empty()) {
it = reactions_.erase(it);
}
return true;
}
break;
}
}
return false;
}
void MessageReactions::sort_reactions(const FlatHashMap<string, size_t> &active_reaction_pos) {
std::sort(reactions_.begin(), reactions_.end(),
[&active_reaction_pos](const MessageReaction &lhs, const MessageReaction &rhs) {
@ -510,6 +748,20 @@ void MessageReactions::fix_chosen_reaction(DialogId my_dialog_id) {
}
}
vector<string> MessageReactions::get_chosen_reactions() const {
if (!chosen_reaction_order_.empty()) {
return chosen_reaction_order_;
}
vector<string> reaction_order;
for (auto &reaction : reactions_) {
if (reaction.is_chosen()) {
reaction_order.push_back(reaction.get_reaction());
}
}
return reaction_order;
}
bool MessageReactions::need_update_message_reactions(const MessageReactions *old_reactions,
const MessageReactions *new_reactions) {
if (old_reactions == nullptr) {
@ -521,7 +773,7 @@ bool MessageReactions::need_update_message_reactions(const MessageReactions *old
return true;
}
// unread_reactions_ are updated independently; compare all other fields
// unread_reactions_ and chosen_reaction_order_ are updated independently; compare all other fields
return old_reactions->reactions_ != new_reactions->reactions_ || old_reactions->is_min_ != new_reactions->is_min_ ||
old_reactions->can_get_added_reactions_ != new_reactions->can_get_added_reactions_ ||
old_reactions->need_polling_ != new_reactions->need_polling_;
@ -537,7 +789,8 @@ bool MessageReactions::need_update_unread_reactions(const MessageReactions *old_
StringBuilder &operator<<(StringBuilder &string_builder, const MessageReactions &reactions) {
return string_builder << (reactions.is_min_ ? "Min" : "") << "MessageReactions{" << reactions.reactions_
<< " with unread " << reactions.unread_reactions_
<< " with unread " << reactions.unread_reactions_ << ", reaction order "
<< reactions.chosen_reaction_order_
<< " and can_get_added_reactions = " << reactions.can_get_added_reactions_;
}
@ -548,6 +801,14 @@ StringBuilder &operator<<(StringBuilder &string_builder, const unique_ptr<Messag
return string_builder << *reactions;
}
bool is_custom_reaction(const string &reaction) {
return reaction[0] == '#';
}
bool is_active_reaction(const string &reaction, const FlatHashMap<string, size_t> &active_reaction_pos) {
return !reaction.empty() && (is_custom_reaction(reaction) || active_reaction_pos.count(reaction) > 0);
}
void reload_message_reactions(Td *td, DialogId dialog_id, vector<MessageId> &&message_ids) {
if (!td->messages_manager_->have_input_peer(dialog_id, AccessRights::Read) || message_ids.empty()) {
return;
@ -561,9 +822,10 @@ void reload_message_reactions(Td *td, DialogId dialog_id, vector<MessageId> &&me
td->create_handler<GetMessagesReactionsQuery>()->send(dialog_id, std::move(message_ids));
}
void set_message_reaction(Td *td, FullMessageId full_message_id, string reaction, bool is_big,
Promise<Unit> &&promise) {
td->create_handler<SendReactionQuery>(std::move(promise))->send(full_message_id, std::move(reaction), is_big);
void send_message_reaction(Td *td, FullMessageId full_message_id, vector<string> reactions, bool is_big,
bool add_to_recent, Promise<Unit> &&promise) {
td->create_handler<SendReactionQuery>(std::move(promise))
->send(full_message_id, std::move(reactions), is_big, add_to_recent);
}
void get_message_added_reactions(Td *td, FullMessageId full_message_id, string reaction, string offset, int32 limit,
@ -591,7 +853,10 @@ void get_message_added_reactions(Td *td, FullMessageId full_message_id, string r
}
void set_default_reaction(Td *td, string reaction, Promise<Unit> &&promise) {
if (!td->stickers_manager_->is_active_reaction(reaction)) {
if (reaction.empty()) {
return promise.set_error(Status::Error(400, "Default reaction must be non-empty"));
}
if (!is_custom_reaction(reaction) && !td->stickers_manager_->is_active_reaction(reaction)) {
return promise.set_error(Status::Error(400, "Can't set incative reaction as default"));
}
@ -599,6 +864,7 @@ void set_default_reaction(Td *td, string reaction, Promise<Unit> &&promise) {
td->option_manager_->set_option_string("default_reaction", reaction);
if (!td->option_manager_->get_option_boolean("default_reaction_needs_sync")) {
td->option_manager_->set_option_boolean("default_reaction_needs_sync", true);
send_set_default_reaction_query(td);
}
}
promise.set_value(Unit());
@ -608,4 +874,75 @@ void send_set_default_reaction_query(Td *td) {
td->create_handler<SetDefaultReactionQuery>()->send(td->option_manager_->get_option_string("default_reaction"));
}
void send_update_default_reaction_type(const string &default_reaction) {
if (default_reaction.empty()) {
LOG(ERROR) << "Have no default reaction";
return;
}
send_closure(G()->td(), &Td::send_update,
td_api::make_object<td_api::updateDefaultReactionType>(get_reaction_type_object(default_reaction)));
}
void report_message_reactions(Td *td, FullMessageId full_message_id, DialogId chooser_dialog_id,
Promise<Unit> &&promise) {
auto dialog_id = full_message_id.get_dialog_id();
if (!td->messages_manager_->have_dialog_force(dialog_id, "send_callback_query")) {
return promise.set_error(Status::Error(400, "Chat not found"));
}
if (!td->messages_manager_->have_input_peer(dialog_id, AccessRights::Read)) {
return promise.set_error(Status::Error(400, "Can't access the chat"));
}
if (!td->messages_manager_->have_message_force(full_message_id, "report_user_reactions")) {
return promise.set_error(Status::Error(400, "Message not found"));
}
auto message_id = full_message_id.get_message_id();
if (message_id.is_valid_scheduled()) {
return promise.set_error(Status::Error(400, "Can't report reactions on scheduled messages"));
}
if (!message_id.is_server()) {
return promise.set_error(Status::Error(400, "Message reactions can't be reported"));
}
if (!td->messages_manager_->have_input_peer(chooser_dialog_id, AccessRights::Know)) {
return promise.set_error(Status::Error(400, "Reaction sender not found"));
}
td->create_handler<ReportReactionQuery>(std::move(promise))->send(dialog_id, message_id, chooser_dialog_id);
}
vector<string> get_recent_reactions(Td *td) {
return td->stickers_manager_->get_recent_reactions();
}
vector<string> get_top_reactions(Td *td) {
return td->stickers_manager_->get_top_reactions();
}
void add_recent_reaction(Td *td, const string &reaction) {
td->stickers_manager_->add_recent_reaction(reaction);
}
int64 get_reactions_hash(const vector<string> &reactions) {
vector<uint64> numbers;
for (auto &reaction : reactions) {
if (is_custom_reaction(reaction)) {
auto custom_emoji_id = static_cast<uint64>(get_custom_emoji_id(reaction));
numbers.push_back(custom_emoji_id >> 32);
numbers.push_back(custom_emoji_id & 0xFFFFFFFF);
} else {
auto emoji = remove_emoji_selectors(reaction);
unsigned char hash[16];
md5(emoji, {hash, sizeof(hash)});
auto get = [hash](int num) {
return static_cast<uint32>(hash[num]);
};
numbers.push_back(0);
numbers.push_back(static_cast<int32>((get(0) << 24) + (get(1) << 16) + (get(2) << 8) + get(3)));
}
}
return get_vector_hash(numbers);
}
} // namespace td

View File

@ -36,11 +36,7 @@ class MessageReaction {
friend StringBuilder &operator<<(StringBuilder &string_builder, const MessageReaction &message_reaction);
public:
static constexpr size_t MAX_RECENT_CHOOSERS = 3;
static constexpr int32 MAX_CHOOSE_COUNT = 2147483640;
MessageReaction() = default;
friend struct MessageReactions;
MessageReaction(string reaction, int32 choose_count, bool is_chosen, vector<DialogId> &&recent_chooser_dialog_ids,
vector<std::pair<ChannelId, MinChannel>> &&recent_chooser_min_channels)
@ -55,15 +51,27 @@ class MessageReaction {
return choose_count_ <= 0;
}
const string &get_reaction() const {
return reaction_;
}
bool is_chosen() const {
return is_chosen_;
}
void set_is_chosen(bool is_chosen, DialogId chooser_dialog_id, bool can_get_added_reactions);
void set_is_chosen(bool is_chosen, DialogId chooser_dialog_id, bool have_recent_choosers);
void add_recent_chooser_dialog_id(DialogId dialog_id);
bool remove_recent_chooser_dialog_id(DialogId dialog_id);
void update_recent_chooser_dialog_ids(const MessageReaction &old_reaction);
public:
static constexpr size_t MAX_RECENT_CHOOSERS = 3;
static constexpr int32 MAX_CHOOSE_COUNT = 2147483640;
MessageReaction() = default;
const string &get_reaction() const {
return reaction_;
}
int32 get_choose_count() const {
return choose_count_;
@ -77,13 +85,8 @@ class MessageReaction {
return recent_chooser_min_channels_;
}
void add_recent_chooser_dialog_id(DialogId dialog_id);
bool remove_recent_chooser_dialog_id(DialogId dialog_id);
void update_recent_chooser_dialog_ids(const MessageReaction &old_reaction);
td_api::object_ptr<td_api::messageReaction> get_message_reaction_object(Td *td) const;
td_api::object_ptr<td_api::messageReaction> get_message_reaction_object(Td *td, UserId my_user_id,
UserId peer_user_id) const;
template <class StorerT>
void store(StorerT &storer) const;
@ -136,6 +139,7 @@ StringBuilder &operator<<(StringBuilder &string_builder, const UnreadMessageReac
struct MessageReactions {
vector<MessageReaction> reactions_;
vector<UnreadMessageReaction> unread_reactions_;
vector<string> chosen_reaction_order_;
bool is_min_ = false;
bool need_polling_ = true;
bool can_get_added_reactions_ = false;
@ -152,10 +156,16 @@ struct MessageReactions {
void update_from(const MessageReactions &old_reactions);
bool add_reaction(const string &reaction, bool is_big, DialogId chooser_dialog_id, bool have_recent_choosers);
bool remove_reaction(const string &reaction, DialogId chooser_dialog_id, bool have_recent_choosers);
void sort_reactions(const FlatHashMap<string, size_t> &active_reaction_pos);
void fix_chosen_reaction(DialogId my_dialog_id);
vector<string> get_chosen_reactions() const;
static bool need_update_message_reactions(const MessageReactions *old_reactions,
const MessageReactions *new_reactions);
@ -167,15 +177,31 @@ struct MessageReactions {
template <class ParserT>
void parse(ParserT &parser);
private:
bool do_remove_reaction(const string &reaction, DialogId chooser_dialog_id, bool have_recent_choosers);
};
StringBuilder &operator<<(StringBuilder &string_builder, const MessageReactions &reactions);
StringBuilder &operator<<(StringBuilder &string_builder, const unique_ptr<MessageReactions> &reactions);
telegram_api::object_ptr<telegram_api::Reaction> get_input_reaction(const string &reaction);
td_api::object_ptr<td_api::ReactionType> get_reaction_type_object(const string &reaction);
string get_message_reaction_string(const telegram_api::object_ptr<telegram_api::Reaction> &reaction);
string get_message_reaction_string(const td_api::object_ptr<td_api::ReactionType> &type);
bool is_custom_reaction(const string &reaction);
bool is_active_reaction(const string &reaction, const FlatHashMap<string, size_t> &active_reaction_pos);
void reload_message_reactions(Td *td, DialogId dialog_id, vector<MessageId> &&message_ids);
void set_message_reaction(Td *td, FullMessageId full_message_id, string reaction, bool is_big, Promise<Unit> &&promise);
void send_message_reaction(Td *td, FullMessageId full_message_id, vector<string> reactions, bool is_big,
bool add_to_recent, Promise<Unit> &&promise);
void get_message_added_reactions(Td *td, FullMessageId full_message_id, string reaction, string offset, int32 limit,
Promise<td_api::object_ptr<td_api::addedReactions>> &&promise);
@ -184,4 +210,17 @@ void set_default_reaction(Td *td, string reaction, Promise<Unit> &&promise);
void send_set_default_reaction_query(Td *td);
void send_update_default_reaction_type(const string &default_reaction);
void report_message_reactions(Td *td, FullMessageId full_message_id, DialogId chooser_dialog_id,
Promise<Unit> &&promise);
vector<string> get_recent_reactions(Td *td);
vector<string> get_top_reactions(Td *td);
void add_recent_reaction(Td *td, const string &reaction);
int64 get_reactions_hash(const vector<string> &reactions);
} // namespace td

View File

@ -51,6 +51,7 @@ void MessageReaction::parse(ParserT &parser) {
td::parse(recent_chooser_min_channels_, parser);
}
CHECK(!is_empty());
CHECK(!reaction_.empty());
}
template <class StorerT>
@ -69,18 +70,21 @@ void UnreadMessageReaction::parse(ParserT &parser) {
END_PARSE_FLAGS();
td::parse(reaction_, parser);
td::parse(sender_dialog_id_, parser);
CHECK(!reaction_.empty());
}
template <class StorerT>
void MessageReactions::store(StorerT &storer) const {
bool has_reactions = !reactions_.empty();
bool has_unread_reactions = !unread_reactions_.empty();
bool has_chosen_reaction_order = !chosen_reaction_order_.empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(is_min_);
STORE_FLAG(need_polling_);
STORE_FLAG(can_get_added_reactions_);
STORE_FLAG(has_unread_reactions);
STORE_FLAG(has_reactions);
STORE_FLAG(has_chosen_reaction_order);
END_STORE_FLAGS();
if (has_reactions) {
td::store(reactions_, storer);
@ -88,18 +92,23 @@ void MessageReactions::store(StorerT &storer) const {
if (has_unread_reactions) {
td::store(unread_reactions_, storer);
}
if (has_chosen_reaction_order) {
td::store(chosen_reaction_order_, storer);
}
}
template <class ParserT>
void MessageReactions::parse(ParserT &parser) {
bool has_reactions;
bool has_unread_reactions;
bool has_chosen_reaction_order;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(is_min_);
PARSE_FLAG(need_polling_);
PARSE_FLAG(can_get_added_reactions_);
PARSE_FLAG(has_unread_reactions);
PARSE_FLAG(has_reactions);
PARSE_FLAG(has_chosen_reaction_order);
END_PARSE_FLAGS();
if (has_reactions) {
td::parse(reactions_, parser);
@ -107,6 +116,9 @@ void MessageReactions::parse(ParserT &parser) {
if (has_unread_reactions) {
td::parse(unread_reactions_, parser);
}
if (has_chosen_reaction_order) {
td::parse(chosen_reaction_order_, parser);
}
}
} // namespace td

File diff suppressed because it is too large Load Diff

View File

@ -8,8 +8,8 @@
#include "td/telegram/AccessRights.h"
#include "td/telegram/AffectedHistory.h"
#include "td/telegram/AvailableReaction.h"
#include "td/telegram/ChannelId.h"
#include "td/telegram/ChatReactions.h"
#include "td/telegram/DialogAction.h"
#include "td/telegram/DialogDate.h"
#include "td/telegram/DialogDb.h"
@ -144,6 +144,7 @@ class MessagesManager final : public Actor {
static constexpr int32 SEND_MESSAGE_FLAG_HAS_MESSAGE = 1 << 11;
static constexpr int32 SEND_MESSAGE_FLAG_HAS_SEND_AS = 1 << 13;
static constexpr int32 SEND_MESSAGE_FLAG_NOFORWARDS = 1 << 14;
static constexpr int32 SEND_MESSAGE_FLAG_UPDATE_STICKER_SETS_ORDER = 1 << 15;
static constexpr int32 ONLINE_MEMBER_COUNT_CACHE_EXPIRE_TIME = 30 * 60;
@ -367,6 +368,8 @@ class MessagesManager final : public Actor {
void on_external_update_message_content(FullMessageId full_message_id);
void on_update_message_content(FullMessageId full_message_id);
void on_read_channel_inbox(ChannelId channel_id, MessageId max_message_id, int32 server_unread_count, int32 pts,
const char *source);
@ -538,9 +541,11 @@ class MessagesManager final : public Actor {
void set_dialog_description(DialogId dialog_id, const string &description, Promise<Unit> &&promise);
void set_active_reactions(vector<AvailableReaction> active_reactions);
void set_active_reactions(vector<string> active_reactions);
void set_dialog_available_reactions(DialogId dialog_id, vector<string> available_reactions, Promise<Unit> &&promise);
void set_dialog_available_reactions(DialogId dialog_id,
td_api::object_ptr<td_api::ChatAvailableReactions> &&available_reactions_ptr,
Promise<Unit> &&promise);
void set_dialog_permissions(DialogId dialog_id, const td_api::object_ptr<td_api::chatPermissions> &permissions,
Promise<Unit> &&promise);
@ -804,9 +809,13 @@ class MessagesManager final : public Actor {
vector<MessageId> get_dialog_scheduled_messages(DialogId dialog_id, bool force, bool ignore_result,
Promise<Unit> &&promise);
Result<vector<AvailableReaction>> get_message_available_reactions(FullMessageId full_message_id);
Result<td_api::object_ptr<td_api::availableReactions>> get_message_available_reactions(FullMessageId full_message_id,
int32 row_size);
void set_message_reaction(FullMessageId full_message_id, string reaction, bool is_big, Promise<Unit> &&promise);
void add_message_reaction(FullMessageId full_message_id, string reaction, bool is_big, bool add_to_recent,
Promise<Unit> &&promise);
void remove_message_reaction(FullMessageId full_message_id, string reaction, Promise<Unit> &&promise);
void get_message_public_forwards(FullMessageId full_message_id, string offset, int32 limit,
Promise<td_api::object_ptr<td_api::foundMessages>> &&promise);
@ -863,7 +872,8 @@ class MessagesManager final : public Actor {
tl_object_ptr<telegram_api::peerNotifySettings> &&peer_notify_settings,
const char *source);
void on_update_dialog_available_reactions(DialogId dialog_id, vector<string> &&available_reactions);
void on_update_dialog_available_reactions(
DialogId dialog_id, telegram_api::object_ptr<telegram_api::ChatReactions> &&available_reactions);
void hide_dialog_action_bar(DialogId dialog_id);
@ -1078,13 +1088,26 @@ class MessagesManager final : public Actor {
}
friend StringBuilder &operator<<(StringBuilder &string_builder, const MessageForwardInfo &forward_info) {
return string_builder << "MessageForwardInfo[" << (forward_info.is_imported ? "imported " : "") << "sender "
<< forward_info.sender_user_id << "(" << forward_info.author_signature << "/"
<< forward_info.sender_name << "), psa_type " << forward_info.psa_type << ", source "
<< forward_info.sender_dialog_id << ", source " << forward_info.message_id << ", from "
<< forward_info.from_dialog_id << ", from " << forward_info.from_message_id << " at "
<< forward_info.date << " "
<< "]";
string_builder << "MessageForwardInfo[" << (forward_info.is_imported ? "imported " : "") << "sender "
<< forward_info.sender_user_id;
if (!forward_info.author_signature.empty() || !forward_info.sender_name.empty()) {
string_builder << '(' << forward_info.author_signature << '/' << forward_info.sender_name << ')';
}
if (!forward_info.psa_type.empty()) {
string_builder << ", psa_type " << forward_info.psa_type;
}
if (forward_info.sender_dialog_id.is_valid()) {
string_builder << ", source ";
if (forward_info.message_id.is_valid()) {
string_builder << FullMessageId(forward_info.sender_dialog_id, forward_info.message_id);
} else {
string_builder << forward_info.sender_dialog_id;
}
}
if (forward_info.from_dialog_id.is_valid() || forward_info.from_message_id.is_valid()) {
string_builder << ", from " << FullMessageId(forward_info.from_dialog_id, forward_info.from_message_id);
}
return string_builder << " at " << forward_info.date << ']';
}
};
@ -1135,6 +1158,7 @@ class MessagesManager final : public Actor {
bool has_explicit_sender = false; // for send_message
bool is_copy = false; // for send_message
bool from_background = false; // for send_message
bool update_stickersets_order = false; // for send_message
bool disable_web_page_preview = false; // for send_message
bool clear_draft = false; // for send_message
bool in_game_share = false; // for send_message
@ -1252,7 +1276,7 @@ class MessagesManager final : public Actor {
MessageId last_pinned_message_id;
MessageId reply_markup_message_id;
DialogNotificationSettings notification_settings;
vector<string> available_reactions;
ChatReactions available_reactions;
uint32 available_reactions_generation = 0;
MessageTtl message_ttl;
unique_ptr<DraftMessage> draft_message;
@ -1270,6 +1294,8 @@ class MessagesManager final : public Actor {
int32 pending_join_request_count = 0;
vector<UserId> pending_join_request_user_ids;
int32 have_full_history_source = 0;
int32 unload_dialog_delay_seed = 0;
int64 last_media_album_id = 0;
FolderId folder_id;
vector<DialogListId> dialog_list_ids; // TODO replace with mask
@ -1704,13 +1730,16 @@ class MessagesManager final : public Actor {
struct MessageSendOptions {
bool disable_notification = false;
bool from_background = false;
bool update_stickersets_order = false;
bool protect_content = false;
int32 schedule_date = 0;
MessageSendOptions() = default;
MessageSendOptions(bool disable_notification, bool from_background, bool protect_content, int32 schedule_date)
MessageSendOptions(bool disable_notification, bool from_background, bool update_stickersets_order,
bool protect_content, int32 schedule_date)
: disable_notification(disable_notification)
, from_background(from_background)
, update_stickersets_order(update_stickersets_order)
, protect_content(protect_content)
, schedule_date(schedule_date) {
}
@ -1864,7 +1893,8 @@ class MessagesManager final : public Actor {
tl_object_ptr<td_api::messageCopyOptions> &&options) const;
Result<MessageSendOptions> process_message_send_options(DialogId dialog_id,
tl_object_ptr<td_api::messageSendOptions> &&options) const;
tl_object_ptr<td_api::messageSendOptions> &&options,
bool allow_update_stickersets_order) const;
static Status can_use_message_send_options(const MessageSendOptions &options,
const unique_ptr<MessageContent> &content, int32 ttl);
@ -1903,6 +1933,8 @@ class MessagesManager final : public Actor {
static Status can_get_media_timestamp_link(DialogId dialog_id, const Message *m);
bool can_report_message_reactions(DialogId dialog_id, const Message *m) const;
Status can_get_message_viewers(FullMessageId full_message_id) TD_WARN_UNUSED_RESULT;
Status can_get_message_viewers(DialogId dialog_id, const Message *m) const TD_WARN_UNUSED_RESULT;
@ -2086,7 +2118,7 @@ class MessagesManager final : public Actor {
int32 get_unload_dialog_delay() const;
int32 get_next_unload_dialog_delay() const;
double get_next_unload_dialog_delay(Dialog *d) const;
void unload_dialog(DialogId dialog_id);
@ -2329,6 +2361,8 @@ class MessagesManager final : public Actor {
void on_message_changed(const Dialog *d, const Message *m, bool need_send_update, const char *source);
void on_message_notification_changed(Dialog *d, const Message *m, const char *source);
bool need_delete_file(FullMessageId full_message_id, FileId file_id) const;
bool need_delete_message_files(DialogId dialog_id, const Message *m) const;
@ -2377,7 +2411,7 @@ class MessagesManager final : public Actor {
static bool need_message_changed_warning(const Message *old_message);
bool update_message_content(DialogId dialog_id, Message *old_message, unique_ptr<MessageContent> new_content,
bool need_send_update_message_content, bool need_merge_files, bool is_message_in_dialog);
bool need_merge_files, bool is_message_in_dialog, bool &is_content_changed);
void update_message_max_reply_media_timestamp(const Dialog *d, Message *m, bool need_send_update_message_content);
@ -2450,8 +2484,6 @@ class MessagesManager final : public Actor {
void send_update_message_send_succeeded(Dialog *d, MessageId old_message_id, const Message *m) const;
void send_update_message_content(DialogId dialog_id, Message *m, bool is_message_in_dialog, const char *source);
void send_update_message_content(const Dialog *d, Message *m, bool is_message_in_dialog, const char *source);
void send_update_message_content_impl(DialogId dialog_id, const Message *m, const char *source) const;
@ -2569,7 +2601,7 @@ class MessagesManager final : public Actor {
void set_dialog_last_read_outbox_message_id(Dialog *d, MessageId message_id);
void set_dialog_last_message_id(Dialog *d, MessageId last_message_id, const char *source);
void set_dialog_last_message_id(Dialog *d, MessageId last_message_id, const char *source, const Message *m = nullptr);
void set_dialog_first_database_message_id(Dialog *d, MessageId first_database_message_id, const char *source);
@ -2598,6 +2630,8 @@ class MessagesManager final : public Actor {
bool set_dialog_is_pinned(DialogListId dialog_list_id, Dialog *d, bool is_pinned,
bool need_update_dialog_lists = true);
void save_pinned_folder_dialog_ids(const DialogList &list) const;
void set_dialog_is_marked_as_unread(Dialog *d, bool is_marked_as_unread);
void set_dialog_is_blocked(Dialog *d, bool is_blocked);
@ -2661,21 +2695,24 @@ class MessagesManager final : public Actor {
bool update_dialog_silent_send_message(Dialog *d, bool silent_send_message);
vector<AvailableReaction> get_message_available_reactions(const Dialog *d, const Message *m);
ChatReactions get_message_available_reactions(const Dialog *d, const Message *m,
bool dissalow_custom_for_non_premium);
void on_set_message_reaction(FullMessageId full_message_id, Result<Unit> result, Promise<Unit> promise);
void set_message_reactions(Dialog *d, Message *m, bool is_big, bool add_to_recent, Promise<Unit> &&promise);
void set_dialog_available_reactions(Dialog *d, vector<string> &&available_reactions);
void on_set_message_reactions(FullMessageId full_message_id, Result<Unit> result, Promise<Unit> promise);
void set_dialog_available_reactions(Dialog *d, ChatReactions &&available_reactions);
void set_dialog_next_available_reactions_generation(Dialog *d, uint32 generation);
void hide_dialog_message_reactions(Dialog *d);
vector<string> get_active_reactions(const vector<string> &available_reactions) const;
ChatReactions get_active_reactions(const ChatReactions &available_reactions) const;
vector<string> get_dialog_active_reactions(const Dialog *d) const;
ChatReactions get_dialog_active_reactions(const Dialog *d) const;
vector<string> get_message_active_reactions(const Dialog *d, const Message *m) const;
ChatReactions get_message_active_reactions(const Dialog *d, const Message *m) const;
static bool need_poll_dialog_message_reactions(const Dialog *d);
@ -2706,7 +2743,8 @@ class MessagesManager final : public Actor {
void fix_new_dialog(Dialog *d, unique_ptr<Message> &&last_database_message, MessageId last_database_message_id,
int64 order, int32 last_clear_history_date, MessageId last_clear_history_message_id,
DialogId default_join_group_call_as_dialog_id, DialogId default_send_message_as_dialog_id,
bool need_drop_default_send_message_as_dialog_id, bool is_loaded_from_database);
bool need_drop_default_send_message_as_dialog_id, bool is_loaded_from_database,
const char *source);
bool add_dialog_last_database_message(Dialog *d, unique_ptr<Message> &&last_database_message);
@ -2763,6 +2801,12 @@ class MessagesManager final : public Actor {
td_api::object_ptr<td_api::chatFilter> get_chat_filter_object(const DialogFilter *filter) const;
void load_dialog_filter_dialogs(DialogFilterId dialog_filter_id, vector<InputDialogId> &&input_dialog_ids,
Promise<Unit> &&promise);
void on_load_dialog_filter_dialogs(DialogFilterId dialog_filter_id, vector<DialogId> &&dialog_ids,
Promise<Unit> &&promise);
void load_dialog_filter(const DialogFilter *filter, bool force, Promise<Unit> &&promise);
void on_get_recommended_dialog_filters(Result<vector<tl_object_ptr<telegram_api::dialogFilterSuggested>>> result,
@ -3485,7 +3529,7 @@ class MessagesManager final : public Actor {
struct CommonDialogs {
vector<DialogId> dialog_ids;
double received_date = 0;
double receive_time = 0;
int32 total_count = 0;
bool is_outdated = false;
};
@ -3701,7 +3745,7 @@ class MessagesManager final : public Actor {
};
FlatHashMap<FullMessageId, PendingReaction, FullMessageIdHash> pending_reactions_;
vector<AvailableReaction> active_reactions_;
vector<string> active_reactions_;
FlatHashMap<string, size_t> active_reaction_pos_;
uint32 scheduled_messages_sync_generation_ = 1;

View File

@ -3332,7 +3332,7 @@ Status NotificationManager::process_push_notification_payload(string payload, bo
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, sender_user_id.get(), sender_access_hash, user_name,
string(), string(), string(), std::move(sender_photo), nullptr, 0, Auto(), string(), string());
string(), string(), string(), std::move(sender_photo), nullptr, 0, Auto(), string(), string(), nullptr);
td_->contacts_manager_->on_get_user(std::move(user), "process_push_notification_payload");
}
@ -3690,7 +3690,7 @@ void NotificationManager::add_message_push_notification(DialogId dialog_id, Mess
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, sender_user_id.get(), 0, user_name, string(), string(),
string(), nullptr, nullptr, 0, Auto(), string(), string());
string(), nullptr, nullptr, 0, Auto(), string(), string(), nullptr);
td_->contacts_manager_->on_get_user(std::move(user), "add_message_push_notification");
}

View File

@ -54,17 +54,18 @@ OptionManager::OptionManager(Td *td)
all_options["utc_time_offset"] = PSTRING() << 'I' << Clocks::tz_offset();
for (const auto &name_value : all_options) {
const string &name = name_value.first;
const string &value = name_value.second;
options_->set(name, value);
options_->set(name, name_value.second);
if (!is_internal_option(name)) {
send_closure(G()->td(), &Td::send_update,
td_api::make_object<td_api::updateOption>(name, get_option_value_object(value)));
td_api::make_object<td_api::updateOption>(name, get_option_value_object(name_value.second)));
} else if (name == "otherwise_relogin_days") {
auto days = narrow_cast<int32>(get_option_integer(name));
if (days > 0) {
vector<SuggestedAction> added_actions{SuggestedAction{SuggestedAction::Type::SetPassword, DialogId(), days}};
send_closure(G()->td(), &Td::send_update, get_update_suggested_actions_object(added_actions, {}));
}
} else if (name == "default_reaction") {
send_update_default_reaction_type(get_option_string(name));
}
}
@ -101,6 +102,11 @@ OptionManager::OptionManager(Td *td)
if (!have_option("chat_filter_chosen_chat_count_max")) {
set_option_integer("chat_filter_chosen_chat_count_max", G()->is_test_dc() ? 5 : 100);
}
if (!have_option("themed_emoji_statuses_sticker_set_id")) {
auto sticker_set_id =
G()->is_test_dc() ? static_cast<int64>(2964141614563343) : static_cast<int64>(773947703670341676);
set_option_integer("themed_emoji_statuses_sticker_set_id", sticker_set_id);
}
}
OptionManager::~OptionManager() = default;
@ -245,7 +251,7 @@ bool OptionManager::is_internal_option(Slice name) {
name == "channels_read_media_period" || name == "chat_read_mark_expire_period" ||
name == "chat_read_mark_size_threshold";
case 'd':
return name == "dc_txt_domain_name" || name == "default_reaction_needs_sync" ||
return name == "dc_txt_domain_name" || name == "default_reaction" || name == "default_reaction_needs_sync" ||
name == "dialog_filters_chats_limit_default" || name == "dialog_filters_chats_limit_premium" ||
name == "dialog_filters_limit_default" || name == "dialog_filters_limit_premium" ||
name == "dialogs_folder_pinned_limit_default" || name == "dialogs_folder_pinned_limit_premium" ||
@ -266,8 +272,9 @@ bool OptionManager::is_internal_option(Slice name) {
case 'p':
return name == "premium_bot_username" || name == "premium_features" || name == "premium_invoice_slug";
case 'r':
return name == "rating_e_decay" || name == "reactions_uniq_max" || name == "recent_stickers_limit" ||
name == "revoke_pm_inbox" || name == "revoke_time_limit" || name == "revoke_pm_time_limit";
return name == "rating_e_decay" || name == "reactions_uniq_max" || name == "reactions_user_max_default" ||
name == "reactions_user_max_premium" || name == "recent_stickers_limit" || name == "revoke_pm_inbox" ||
name == "revoke_time_limit" || name == "revoke_pm_time_limit";
case 's':
return name == "saved_animations_limit" || name == "saved_gifs_limit_default" ||
name == "saved_gifs_limit_premium" || name == "session_count" || name == "stickers_faved_limit_default" ||
@ -317,8 +324,8 @@ void OptionManager::on_option_updated(Slice name) {
}
break;
case 'd':
if (name == "default_reaction_needs_sync" && get_option_boolean(name)) {
send_set_default_reaction_query(td_);
if (name == "default_reaction") {
send_update_default_reaction_type(get_option_string(name));
}
if (name == "dice_emojis") {
send_closure(td_->stickers_manager_actor_, &StickersManager::on_update_dice_emojis);
@ -504,7 +511,7 @@ td_api::object_ptr<td_api::OptionValue> OptionManager::get_option_synchronously(
break;
case 'v':
if (name == "version") {
return td_api::make_object<td_api::optionValueString>("1.8.5");
return td_api::make_object<td_api::optionValueString>("1.8.6");
}
break;
}
@ -621,14 +628,6 @@ void OptionManager::set_option(const string &name, td_api::object_ptr<td_api::Op
}
break;
case 'd':
if (!is_bot && name == "default_reaction") {
string reaction;
if (value_constructor_id == td_api::optionValueString::ID) {
reaction = static_cast<td_api::optionValueString *>(value.get())->value_;
}
set_default_reaction(td_, std::move(reaction), std::move(promise));
return;
}
if (!is_bot && set_boolean_option("disable_animated_emoji")) {
return;
}

View File

@ -180,6 +180,46 @@ void PasswordManager::set_password(string current_password, string new_password,
update_password_settings(std::move(update_settings), std::move(promise));
}
void PasswordManager::set_login_email_address(string new_login_email_address, Promise<SentEmailCode> promise) {
last_set_login_email_address_ = new_login_email_address;
auto query = G()->net_query_creator().create(telegram_api::account_sendVerifyEmailCode(
make_tl_object<telegram_api::emailVerifyPurposeLoginChange>(), std::move(new_login_email_address)));
send_with_promise(std::move(query),
PromiseCreator::lambda([promise = std::move(promise)](Result<NetQueryPtr> r_query) mutable {
auto r_result = fetch_result<telegram_api::account_sendVerifyEmailCode>(std::move(r_query));
if (r_result.is_error()) {
return promise.set_error(r_result.move_as_error());
}
return promise.set_value(SentEmailCode(r_result.move_as_ok()));
}));
}
void PasswordManager::resend_login_email_address_code(Promise<SentEmailCode> promise) {
if (last_set_login_email_address_.empty()) {
return promise.set_error(Status::Error(400, "No login email address code was sent"));
}
set_login_email_address(last_set_login_email_address_, std::move(promise));
}
void PasswordManager::check_login_email_address_code(EmailVerification &&code, Promise<Unit> promise) {
if (last_set_login_email_address_.empty()) {
return promise.set_error(Status::Error(400, "No login email address code was sent"));
}
if (code.is_empty()) {
return promise.set_error(Status::Error(400, "Verification code must be non-empty"));
}
auto query = G()->net_query_creator().create(telegram_api::account_verifyEmail(
make_tl_object<telegram_api::emailVerifyPurposeLoginChange>(), code.get_input_email_verification()));
send_with_promise(std::move(query),
PromiseCreator::lambda([promise = std::move(promise)](Result<NetQueryPtr> r_query) mutable {
auto r_result = fetch_result<telegram_api::account_verifyEmail>(std::move(r_query));
if (r_result.is_error()) {
return promise.set_error(r_result.move_as_error());
}
return promise.set_value(Unit());
}));
}
void PasswordManager::set_recovery_email_address(string password, string new_recovery_email_address,
Promise<State> promise) {
UpdateSettings update_settings;
@ -420,28 +460,21 @@ void PasswordManager::resend_recovery_email_address_code(Promise<State> promise)
}));
}
void PasswordManager::send_email_address_verification_code(
string email, Promise<td_api::object_ptr<td_api::emailAddressAuthenticationCodeInfo>> promise) {
void PasswordManager::send_email_address_verification_code(string email, Promise<SentEmailCode> promise) {
last_verified_email_address_ = email;
auto query = G()->net_query_creator().create(telegram_api::account_sendVerifyEmailCode(std::move(email)));
send_with_promise(
std::move(query), PromiseCreator::lambda([promise = std::move(promise)](Result<NetQueryPtr> r_query) mutable {
auto r_result = fetch_result<telegram_api::account_sendVerifyEmailCode>(std::move(r_query));
if (r_result.is_error()) {
return promise.set_error(r_result.move_as_error());
}
auto result = r_result.move_as_ok();
if (result->length_ < 0 || result->length_ >= 100) {
LOG(ERROR) << "Receive wrong code length " << result->length_;
result->length_ = 0;
}
return promise.set_value(
make_tl_object<td_api::emailAddressAuthenticationCodeInfo>(result->email_pattern_, result->length_));
}));
auto query = G()->net_query_creator().create(telegram_api::account_sendVerifyEmailCode(
make_tl_object<telegram_api::emailVerifyPurposePassport>(), std::move(email)));
send_with_promise(std::move(query),
PromiseCreator::lambda([promise = std::move(promise)](Result<NetQueryPtr> r_query) mutable {
auto r_result = fetch_result<telegram_api::account_sendVerifyEmailCode>(std::move(r_query));
if (r_result.is_error()) {
return promise.set_error(r_result.move_as_error());
}
return promise.set_value(SentEmailCode(r_result.move_as_ok()));
}));
}
void PasswordManager::resend_email_address_verification_code(
Promise<td_api::object_ptr<td_api::emailAddressAuthenticationCodeInfo>> promise) {
void PasswordManager::resend_email_address_verification_code(Promise<SentEmailCode> promise) {
if (last_verified_email_address_.empty()) {
return promise.set_error(Status::Error(400, "No email address verification was sent"));
}
@ -452,8 +485,9 @@ void PasswordManager::check_email_address_verification_code(string code, Promise
if (last_verified_email_address_.empty()) {
return promise.set_error(Status::Error(400, "No email address verification was sent"));
}
auto query =
G()->net_query_creator().create(telegram_api::account_verifyEmail(last_verified_email_address_, std::move(code)));
auto verification_code = make_tl_object<telegram_api::emailVerificationCode>(std::move(code));
auto query = G()->net_query_creator().create(telegram_api::account_verifyEmail(
make_tl_object<telegram_api::emailVerifyPurposePassport>(), std::move(verification_code)));
send_with_promise(std::move(query),
PromiseCreator::lambda([promise = std::move(promise)](Result<NetQueryPtr> r_query) mutable {
auto r_result = fetch_result<telegram_api::account_verifyEmail>(std::move(r_query));
@ -464,19 +498,17 @@ void PasswordManager::check_email_address_verification_code(string code, Promise
}));
}
void PasswordManager::request_password_recovery(
Promise<td_api::object_ptr<td_api::emailAddressAuthenticationCodeInfo>> promise) {
void PasswordManager::request_password_recovery(Promise<SentEmailCode> promise) {
// is called only after authorization
send_with_promise(
G()->net_query_creator().create(telegram_api::auth_requestPasswordRecovery()),
PromiseCreator::lambda([promise = std::move(promise)](Result<NetQueryPtr> r_query) mutable {
auto r_result = fetch_result<telegram_api::auth_requestPasswordRecovery>(std::move(r_query));
if (r_result.is_error()) {
return promise.set_error(r_result.move_as_error());
}
auto result = r_result.move_as_ok();
return promise.set_value(make_tl_object<td_api::emailAddressAuthenticationCodeInfo>(result->email_pattern_, 0));
}));
send_with_promise(G()->net_query_creator().create(telegram_api::auth_requestPasswordRecovery()),
PromiseCreator::lambda([promise = std::move(promise)](Result<NetQueryPtr> r_query) mutable {
auto r_result = fetch_result<telegram_api::auth_requestPasswordRecovery>(std::move(r_query));
if (r_result.is_error()) {
return promise.set_error(r_result.move_as_error());
}
auto result = r_result.move_as_ok();
return promise.set_value(SentEmailCode(std::move(result->email_pattern_), 0));
}));
}
void PasswordManager::check_password_recovery_code(string code, Promise<Unit> promise) {
@ -793,8 +825,8 @@ void PasswordManager::do_get_state(Promise<PasswordState> promise) {
state.has_password = false;
send_closure(actor_id, &PasswordManager::drop_cached_secret);
}
state.unconfirmed_recovery_email_address_pattern = std::move(password->email_unconfirmed_pattern_);
state.code_length = code_length;
state.unconfirmed_recovery_email_code = {std::move(password->email_unconfirmed_pattern_), code_length};
state.login_email_pattern = std::move(password->login_email_pattern_);
if (password->flags_ & telegram_api::account_password::PENDING_RESET_DATE_MASK) {
state.pending_reset_date = td::max(password->pending_reset_date_, 0);

View File

@ -6,9 +6,11 @@
//
#pragma once
#include "td/telegram/EmailVerification.h"
#include "td/telegram/net/NetQuery.h"
#include "td/telegram/NewPasswordState.h"
#include "td/telegram/SecureStorage.h"
#include "td/telegram/SentEmailCode.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
@ -69,18 +71,21 @@ class PasswordManager final : public NetQueryCallback {
void get_state(Promise<State> promise);
void set_password(string current_password, string new_password, string new_hint, bool set_recovery_email_address,
string recovery_email_address, Promise<State> promise);
void set_login_email_address(string new_login_email_address, Promise<SentEmailCode> promise);
void resend_login_email_address_code(Promise<SentEmailCode> promise);
void check_login_email_address_code(EmailVerification &&code, Promise<Unit> promise);
void set_recovery_email_address(string password, string new_recovery_email_address, Promise<State> promise);
void get_recovery_email_address(string password, Promise<tl_object_ptr<td_api::recoveryEmailAddress>> promise);
void check_recovery_email_address_code(string code, Promise<State> promise);
void resend_recovery_email_address_code(Promise<State> promise);
void send_email_address_verification_code(
string email, Promise<td_api::object_ptr<td_api::emailAddressAuthenticationCodeInfo>> promise);
void resend_email_address_verification_code(
Promise<td_api::object_ptr<td_api::emailAddressAuthenticationCodeInfo>> promise);
void send_email_address_verification_code(string email, Promise<SentEmailCode> promise);
void resend_email_address_verification_code(Promise<SentEmailCode> promise);
void check_email_address_verification_code(string code, Promise<Unit> promise);
void request_password_recovery(Promise<td_api::object_ptr<td_api::emailAddressAuthenticationCodeInfo>> promise);
void request_password_recovery(Promise<SentEmailCode> promise);
void check_password_recovery_code(string code, Promise<Unit> promise);
void recover_password(string code, string new_password, string new_hint, Promise<State> promise);
@ -106,8 +111,8 @@ class PasswordManager final : public NetQueryCallback {
string password_hint;
bool has_recovery_email_address = false;
bool has_secure_values = false;
string unconfirmed_recovery_email_address_pattern;
int32 code_length = 0;
SentEmailCode unconfirmed_recovery_email_code;
string login_email_pattern;
int32 pending_reset_date = 0;
string current_client_salt;
@ -120,13 +125,10 @@ class PasswordManager final : public NetQueryCallback {
NewPasswordState new_state;
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>(
unconfirmed_recovery_email_address_pattern, code_length);
}
return td_api::make_object<td_api::passwordState>(has_password, password_hint, has_recovery_email_address,
has_secure_values, std::move(code_info), pending_reset_date);
return td_api::make_object<td_api::passwordState>(
has_password, password_hint, has_recovery_email_address, has_secure_values,
unconfirmed_recovery_email_code.get_email_address_authentication_code_info_object(), login_email_pattern,
pending_reset_date);
}
};
@ -159,6 +161,7 @@ class PasswordManager final : public NetQueryCallback {
TempPasswordState temp_password_state_;
Promise<TempState> create_temp_password_promise_;
string last_set_login_email_address_;
string last_verified_email_address_;
int32 last_code_length_ = 0;

View File

@ -45,7 +45,7 @@ void PhoneNumberManager::send_new_send_code_query(uint64 query_id, const telegra
void PhoneNumberManager::set_phone_number(uint64 query_id, string phone_number, Settings settings) {
if (phone_number.empty()) {
return on_query_error(query_id, Status::Error(400, "Phone number can't be empty"));
return on_query_error(query_id, Status::Error(400, "Phone number must be non-empty"));
}
switch (type_) {
@ -64,10 +64,10 @@ void PhoneNumberManager::set_phone_number(uint64 query_id, string phone_number,
void PhoneNumberManager::set_phone_number_and_hash(uint64 query_id, string hash, string phone_number,
Settings settings) {
if (phone_number.empty()) {
return on_query_error(query_id, Status::Error(400, "Phone number can't be empty"));
return on_query_error(query_id, Status::Error(400, "Phone number must be non-empty"));
}
if (hash.empty()) {
return on_query_error(query_id, Status::Error(400, "Hash can't be empty"));
return on_query_error(query_id, Status::Error(400, "Hash must be non-empty"));
}
switch (type_) {
@ -213,6 +213,14 @@ void PhoneNumberManager::on_send_code_result(NetQueryPtr &result) {
LOG(INFO) << "Receive " << to_string(sent_code);
switch (sent_code->type_->get_id()) {
case telegram_api::auth_sentCodeTypeSetUpEmailRequired::ID:
case telegram_api::auth_sentCodeTypeEmailCode::ID:
return on_query_error(Status::Error(500, "Receive incorrect response"));
default:
break;
}
send_code_helper_.on_sent_code(std::move(sent_code));
state_ = State::WaitCode;

View File

@ -45,7 +45,7 @@ static td_api::object_ptr<td_api::PremiumFeature> get_premium_feature_object(Sli
if (premium_feature == "no_ads") {
return td_api::make_object<td_api::premiumFeatureDisabledAds>();
}
if (premium_feature == "unique_reactions") {
if (premium_feature == "unique_reactions" || premium_feature == "infinite_reactions") {
return td_api::make_object<td_api::premiumFeatureUniqueReactions>();
}
if (premium_feature == "premium_stickers") {
@ -60,6 +60,9 @@ static td_api::object_ptr<td_api::PremiumFeature> get_premium_feature_object(Sli
if (premium_feature == "profile_badge") {
return td_api::make_object<td_api::premiumFeatureProfileBadge>();
}
if (premium_feature == "emoji_status") {
return td_api::make_object<td_api::premiumFeatureEmojiStatus>();
}
if (premium_feature == "animated_userpics") {
return td_api::make_object<td_api::premiumFeatureAnimatedProfilePhoto>();
}
@ -130,14 +133,6 @@ class GetPremiumPromoQuery final : public Td::ResultHandler {
return on_error(Status::Error(500, "Receive wrong number of videos"));
}
if (promo->monthly_amount_ < 0 || !check_currency_amount(promo->monthly_amount_)) {
return on_error(Status::Error(500, "Receive invalid monthly amount"));
}
if (promo->currency_.size() != 3) {
return on_error(Status::Error(500, "Receive invalid currency"));
}
vector<td_api::object_ptr<td_api::premiumFeaturePromotionAnimation>> animations;
for (size_t i = 0; i < promo->video_sections_.size(); i++) {
auto feature = get_premium_feature_object(promo->video_sections_[i]);
@ -164,8 +159,9 @@ class GetPremiumPromoQuery final : public Td::ResultHandler {
std::move(animation_object)));
}
auto period_options = get_premium_gift_options(std::move(promo->period_options_));
promise_.set_value(td_api::make_object<td_api::premiumState>(get_formatted_text_object(state, true, 0),
std::move(promo->currency_), promo->monthly_amount_,
get_premium_payment_options_object(period_options),
std::move(animations)));
}
@ -349,7 +345,7 @@ static string get_premium_source(const td_api::PremiumFeature *feature) {
case td_api::premiumFeatureDisabledAds::ID:
return "no_ads";
case td_api::premiumFeatureUniqueReactions::ID:
return "unique_reactions";
return "infinite_reactions";
case td_api::premiumFeatureUniqueStickers::ID:
return "premium_stickers";
case td_api::premiumFeatureCustomEmoji::ID:
@ -358,6 +354,8 @@ static string get_premium_source(const td_api::PremiumFeature *feature) {
return "advanced_chat_management";
case td_api::premiumFeatureProfileBadge::ID:
return "profile_badge";
case td_api::premiumFeatureEmojiStatus::ID:
return "emoji_status";
case td_api::premiumFeatureAnimatedProfilePhoto::ID:
return "animated_userpics";
case td_api::premiumFeatureAppIcons::ID:
@ -450,12 +448,12 @@ void get_premium_limit(const td_api::object_ptr<td_api::PremiumLimitType> &limit
void get_premium_features(Td *td, const td_api::object_ptr<td_api::PremiumSource> &source,
Promise<td_api::object_ptr<td_api::premiumFeatures>> &&promise) {
auto premium_features =
full_split(G()->get_option_string(
"premium_features",
"double_limits,more_upload,faster_download,voice_to_text,no_ads,unique_reactions,premium_stickers,"
"animated_emoji,advanced_chat_management,profile_badge,animated_userpics,app_icons"),
',');
auto premium_features = full_split(
G()->get_option_string(
"premium_features",
"double_limits,more_upload,faster_download,voice_to_text,no_ads,infinite_reactions,premium_stickers,"
"animated_emoji,advanced_chat_management,profile_badge,emoji_status,animated_userpics,app_icons"),
',');
vector<td_api::object_ptr<td_api::PremiumFeature>> features;
for (const auto &premium_feature : premium_features) {
auto feature = get_premium_feature_object(premium_feature);

View File

@ -9,9 +9,11 @@
#include "td/telegram/LinkManager.h"
#include "td/telegram/Payments.h"
#include "td/utils/algorithm.h"
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include <algorithm>
#include <tuple>
namespace td {
@ -22,17 +24,33 @@ PremiumGiftOption::PremiumGiftOption(telegram_api::object_ptr<telegram_api::prem
, amount_(option->amount_)
, bot_url_(std::move(option->bot_url_))
, store_product_(std::move(option->store_product_)) {
}
PremiumGiftOption::PremiumGiftOption(telegram_api::object_ptr<telegram_api::premiumSubscriptionOption> &&option)
: months_(option->months_)
, currency_(std::move(option->currency_))
, amount_(option->amount_)
, bot_url_(std::move(option->bot_url_))
, store_product_(std::move(option->store_product_)) {
}
bool PremiumGiftOption::is_valid() const {
if (amount_ <= 0 || !check_currency_amount(amount_)) {
LOG(ERROR) << "Receive invalid premium gift option amount " << amount_;
amount_ = static_cast<int64>(1) << 40;
LOG(ERROR) << "Receive invalid premium payment option amount " << amount_;
return false;
}
if (currency_.size() != 3) {
LOG(ERROR) << "Receive invalid premium payment option currency " << currency_;
return false;
}
return true;
}
double PremiumGiftOption::get_monthly_price() const {
return static_cast<double>(amount_) / static_cast<double>(months_);
}
td_api::object_ptr<td_api::premiumGiftOption> PremiumGiftOption::get_premium_gift_option_object(
td_api::object_ptr<td_api::premiumPaymentOption> PremiumGiftOption::get_premium_payment_option_object(
const PremiumGiftOption &base_option) const {
auto link_type = LinkManager::parse_internal_link(bot_url_, true);
int32 discount_percentage = 0;
@ -42,7 +60,7 @@ td_api::object_ptr<td_api::premiumGiftOption> PremiumGiftOption::get_premium_gif
discount_percentage = static_cast<int32>(100 * (1.0 - relative_price));
}
}
return td_api::make_object<td_api::premiumGiftOption>(
return td_api::make_object<td_api::premiumPaymentOption>(
currency_, amount_, discount_percentage, months_, store_product_,
link_type == nullptr ? nullptr : link_type->get_internal_link_type_object());
}
@ -61,4 +79,31 @@ bool operator!=(const PremiumGiftOption &lhs, const PremiumGiftOption &rhs) {
return !(lhs == rhs);
}
vector<PremiumGiftOption> get_premium_gift_options(
vector<telegram_api::object_ptr<telegram_api::premiumGiftOption>> &&options) {
auto premium_gift_options = transform(
std::move(options), [](auto &&premium_gift_option) { return PremiumGiftOption(std::move(premium_gift_option)); });
td::remove_if(premium_gift_options, [](const auto &premium_gift_option) { return !premium_gift_option.is_valid(); });
return premium_gift_options;
}
vector<PremiumGiftOption> get_premium_gift_options(
vector<telegram_api::object_ptr<telegram_api::premiumSubscriptionOption>> &&options) {
auto premium_gift_options = transform(
std::move(options), [](auto &&premium_gift_option) { return PremiumGiftOption(std::move(premium_gift_option)); });
td::remove_if(premium_gift_options, [](const auto &premium_gift_option) { return !premium_gift_option.is_valid(); });
return premium_gift_options;
}
vector<td_api::object_ptr<td_api::premiumPaymentOption>> get_premium_payment_options_object(
const vector<PremiumGiftOption> &options) {
if (options.empty()) {
return {};
}
auto base_premium_option_it = std::min_element(options.begin(), options.end());
return transform(options, [&base_premium_option_it](const auto &option) {
return option.get_premium_payment_option_object(*base_premium_option_it);
});
}
} // namespace td

View File

@ -29,10 +29,13 @@ class PremiumGiftOption {
public:
PremiumGiftOption() = default;
explicit PremiumGiftOption(telegram_api::object_ptr<telegram_api::premiumGiftOption> &&option);
explicit PremiumGiftOption(telegram_api::object_ptr<telegram_api::premiumSubscriptionOption> &&option);
td_api::object_ptr<td_api::premiumGiftOption> get_premium_gift_option_object(
td_api::object_ptr<td_api::premiumPaymentOption> get_premium_payment_option_object(
const PremiumGiftOption &base_option) const;
bool is_valid() const;
template <class StorerT>
void store(StorerT &storer) const;
@ -43,4 +46,13 @@ class PremiumGiftOption {
bool operator==(const PremiumGiftOption &lhs, const PremiumGiftOption &rhs);
bool operator!=(const PremiumGiftOption &lhs, const PremiumGiftOption &rhs);
vector<PremiumGiftOption> get_premium_gift_options(
vector<telegram_api::object_ptr<telegram_api::premiumGiftOption>> &&options);
vector<PremiumGiftOption> get_premium_gift_options(
vector<telegram_api::object_ptr<telegram_api::premiumSubscriptionOption>> &&options);
vector<td_api::object_ptr<td_api::premiumPaymentOption>> get_premium_payment_options_object(
const vector<PremiumGiftOption> &options);
} // namespace td

View File

@ -500,7 +500,7 @@ static Result<InlineKeyboardButton> get_inline_keyboard_button(tl_object_ptr<td_
current_button.text = std::move(button->text_);
if (button->type_ == nullptr) {
return Status::Error(400, "Inline keyboard button type can't be empty");
return Status::Error(400, "Inline keyboard button type must be non-empty");
}
int32 button_type_id = button->type_->get_id();

View File

@ -12,10 +12,15 @@
namespace td {
void SendCodeHelper::on_sent_code(telegram_api::object_ptr<telegram_api::auth_sentCode> sent_code) {
phone_code_hash_ = sent_code->phone_code_hash_;
sent_code_info_ = get_authentication_code_info(std::move(sent_code->type_));
phone_code_hash_ = std::move(sent_code->phone_code_hash_);
sent_code_info_ = get_sent_authentication_code_info(std::move(sent_code->type_));
next_code_info_ = get_authentication_code_info(std::move(sent_code->next_type_));
next_code_timestamp_ = Timestamp::in((sent_code->flags_ & SENT_CODE_FLAG_HAS_TIMEOUT) != 0 ? sent_code->timeout_ : 0);
next_code_timestamp_ =
Time::now() + ((sent_code->flags_ & SENT_CODE_FLAG_HAS_TIMEOUT) != 0 ? sent_code->timeout_ : 0);
}
void SendCodeHelper::on_phone_code_hash(string &&phone_code_hash) {
phone_code_hash_ = std::move(phone_code_hash);
}
td_api::object_ptr<td_api::authorizationStateWaitCode> SendCodeHelper::get_authorization_state_wait_code() const {
@ -26,7 +31,7 @@ td_api::object_ptr<td_api::authenticationCodeInfo> SendCodeHelper::get_authentic
return make_tl_object<td_api::authenticationCodeInfo>(
phone_number_, get_authentication_code_type_object(sent_code_info_),
get_authentication_code_type_object(next_code_info_),
max(static_cast<int32>(next_code_timestamp_.in() + 1 - 1e-9), 0));
max(static_cast<int32>(next_code_timestamp_ - Time::now() + 1 - 1e-9), 0));
}
Result<telegram_api::auth_resendCode> SendCodeHelper::resend_code() const {
@ -76,6 +81,10 @@ telegram_api::auth_sendCode SendCodeHelper::send_code(string phone_number, const
return telegram_api::auth_sendCode(phone_number_, api_id, api_hash, get_input_code_settings(settings));
}
telegram_api::account_sendVerifyEmailCode SendCodeHelper::send_verify_email_code(const string &email_address) {
return telegram_api::account_sendVerifyEmailCode(get_email_verify_purpose_login_setup(), email_address);
}
telegram_api::account_sendChangePhoneCode SendCodeHelper::send_change_phone_code(Slice phone_number,
const Settings &settings) {
phone_number_ = phone_number.str();
@ -116,7 +125,7 @@ SendCodeHelper::AuthenticationCodeInfo SendCodeHelper::get_authentication_code_i
}
}
SendCodeHelper::AuthenticationCodeInfo SendCodeHelper::get_authentication_code_info(
SendCodeHelper::AuthenticationCodeInfo SendCodeHelper::get_sent_authentication_code_info(
tl_object_ptr<telegram_api::auth_SentCodeType> &&sent_code_type_ptr) {
CHECK(sent_code_type_ptr != nullptr);
switch (sent_code_type_ptr->get_id()) {
@ -141,6 +150,8 @@ SendCodeHelper::AuthenticationCodeInfo SendCodeHelper::get_authentication_code_i
return AuthenticationCodeInfo{AuthenticationCodeInfo::Type::MissedCall, code_type->length_,
std::move(code_type->prefix_)};
}
case telegram_api::auth_sentCodeTypeEmailCode::ID:
case telegram_api::auth_sentCodeTypeSetUpEmailRequired::ID:
default:
UNREACHABLE();
return AuthenticationCodeInfo();
@ -169,4 +180,9 @@ td_api::object_ptr<td_api::AuthenticationCodeType> SendCodeHelper::get_authentic
}
}
telegram_api::object_ptr<telegram_api::emailVerifyPurposeLoginSetup>
SendCodeHelper::get_email_verify_purpose_login_setup() const {
return telegram_api::make_object<telegram_api::emailVerifyPurposeLoginSetup>(phone_number_, phone_code_hash_);
}
} // namespace td

View File

@ -20,6 +20,8 @@ class SendCodeHelper {
public:
void on_sent_code(telegram_api::object_ptr<telegram_api::auth_sentCode> sent_code);
void on_phone_code_hash(string &&phone_code_hash);
td_api::object_ptr<td_api::authorizationStateWaitCode> get_authorization_state_wait_code() const;
td_api::object_ptr<td_api::authenticationCodeInfo> get_authentication_code_info_object() const;
@ -31,6 +33,8 @@ class SendCodeHelper {
telegram_api::auth_sendCode send_code(string phone_number, const Settings &settings, int32 api_id,
const string &api_hash);
telegram_api::account_sendVerifyEmailCode send_verify_email_code(const string &email_address);
telegram_api::account_sendChangePhoneCode send_change_phone_code(Slice phone_number, const Settings &settings);
telegram_api::account_sendVerifyPhoneCode send_verify_phone_code(Slice phone_number, const Settings &settings);
@ -38,6 +42,8 @@ class SendCodeHelper {
telegram_api::account_sendConfirmPhoneCode send_confirm_phone_code(const string &hash, Slice phone_number,
const Settings &settings);
telegram_api::object_ptr<telegram_api::emailVerifyPurposeLoginSetup> get_email_verify_purpose_login_setup() const;
Slice phone_number() const {
return phone_number_;
}
@ -77,11 +83,11 @@ class SendCodeHelper {
SendCodeHelper::AuthenticationCodeInfo sent_code_info_;
SendCodeHelper::AuthenticationCodeInfo next_code_info_;
Timestamp next_code_timestamp_;
double next_code_timestamp_ = 0.0;
static AuthenticationCodeInfo get_authentication_code_info(
tl_object_ptr<telegram_api::auth_CodeType> &&code_type_ptr);
static AuthenticationCodeInfo get_authentication_code_info(
static AuthenticationCodeInfo get_sent_authentication_code_info(
tl_object_ptr<telegram_api::auth_SentCodeType> &&sent_code_type_ptr);
static td_api::object_ptr<td_api::AuthenticationCodeType> get_authentication_code_type_object(

View File

@ -6,6 +6,7 @@
//
#pragma once
#include "td/telegram/logevent/LogEventHelper.h"
#include "td/telegram/SendCodeHelper.h"
#include "td/utils/tl_helpers.h"
@ -36,7 +37,7 @@ void SendCodeHelper::store(StorerT &storer) const {
store(phone_code_hash_, storer);
store(sent_code_info_, storer);
store(next_code_info_, storer);
store(next_code_timestamp_, storer);
store_time(next_code_timestamp_, storer);
}
template <class ParserT>
@ -48,7 +49,7 @@ void SendCodeHelper::parse(ParserT &parser) {
parse(phone_code_hash_, parser);
parse(sent_code_info_, parser);
parse(next_code_info_, parser);
parse(next_code_timestamp_, parser);
parse_time(next_code_timestamp_, parser);
}
} // namespace td

View File

@ -0,0 +1,27 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/SentEmailCode.h"
namespace td {
SentEmailCode::SentEmailCode(telegram_api::object_ptr<telegram_api::account_sentEmailCode> &&email_code)
: email_address_pattern_(std::move(email_code->email_pattern_)), code_length_(email_code->length_) {
if (code_length_ < 0 || code_length_ >= 100) {
LOG(ERROR) << "Receive wrong email code length " << code_length_;
code_length_ = 0;
}
}
td_api::object_ptr<td_api::emailAddressAuthenticationCodeInfo>
SentEmailCode::get_email_address_authentication_code_info_object() const {
if (is_empty()) {
return nullptr;
}
return td_api::make_object<td_api::emailAddressAuthenticationCodeInfo>(email_address_pattern_, code_length_);
}
} // namespace td

View File

@ -0,0 +1,50 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/utils/common.h"
#include "td/utils/tl_helpers.h"
namespace td {
class SentEmailCode {
string email_address_pattern_;
int32 code_length_ = 0;
public:
SentEmailCode() = default;
SentEmailCode(string &&email_address_pattern, int32 code_length)
: email_address_pattern_(std::move(email_address_pattern)), code_length_(code_length) {
}
explicit SentEmailCode(telegram_api::object_ptr<telegram_api::account_sentEmailCode> &&email_code);
td_api::object_ptr<td_api::emailAddressAuthenticationCodeInfo> get_email_address_authentication_code_info_object()
const;
bool is_empty() const {
return email_address_pattern_.empty();
}
template <class StorerT>
void store(StorerT &storer) const {
td::store(email_address_pattern_, storer);
td::store(code_length_, storer);
}
template <class ParserT>
void parse(ParserT &parser) {
td::parse(email_address_pattern_, parser);
td::parse(code_length_, parser);
}
};
} // namespace td

View File

@ -29,6 +29,14 @@ SpecialStickerSetType SpecialStickerSetType::premium_gifts() {
return SpecialStickerSetType("premium_gifts_sticker_set");
}
SpecialStickerSetType SpecialStickerSetType::generic_animations() {
return SpecialStickerSetType("generic_animations_sticker_set");
}
SpecialStickerSetType SpecialStickerSetType::default_statuses() {
return SpecialStickerSetType("default_statuses_sticker_set");
}
SpecialStickerSetType::SpecialStickerSetType(
const telegram_api::object_ptr<telegram_api::InputStickerSet> &input_sticker_set) {
CHECK(input_sticker_set != nullptr);
@ -45,6 +53,12 @@ SpecialStickerSetType::SpecialStickerSetType(
case telegram_api::inputStickerSetPremiumGifts::ID:
*this = premium_gifts();
break;
case telegram_api::inputStickerSetEmojiGenericAnimations::ID:
*this = generic_animations();
break;
case telegram_api::inputStickerSetEmojiDefaultStatuses::ID:
*this = default_statuses();
break;
default:
UNREACHABLE();
break;
@ -69,6 +83,12 @@ telegram_api::object_ptr<telegram_api::InputStickerSet> SpecialStickerSetType::g
if (*this == premium_gifts()) {
return telegram_api::make_object<telegram_api::inputStickerSetPremiumGifts>();
}
if (*this == generic_animations()) {
return telegram_api::make_object<telegram_api::inputStickerSetEmojiGenericAnimations>();
}
if (*this == default_statuses()) {
return telegram_api::make_object<telegram_api::inputStickerSetEmojiDefaultStatuses>();
}
auto emoji = get_dice_emoji();
if (!emoji.empty()) {
return telegram_api::make_object<telegram_api::inputStickerSetDice>(emoji);

View File

@ -31,6 +31,10 @@ class SpecialStickerSetType {
static SpecialStickerSetType premium_gifts();
static SpecialStickerSetType generic_animations();
static SpecialStickerSetType default_statuses();
string get_dice_emoji() const;
bool is_empty() const {

View File

@ -8,6 +8,16 @@
namespace td {
StickerType get_sticker_type(bool is_mask, bool is_custom_emoji) {
if (is_custom_emoji) {
return StickerType::CustomEmoji;
}
if (is_mask) {
return StickerType::Mask;
}
return StickerType::Regular;
}
StickerType get_sticker_type(const td_api::object_ptr<td_api::StickerType> &type) {
if (type == nullptr) {
return StickerType::Regular;

View File

@ -18,6 +18,8 @@ enum class StickerType : int32 { Regular, Mask, CustomEmoji };
static constexpr int32 MAX_STICKER_TYPE = 3;
StickerType get_sticker_type(bool is_mask, bool is_custom_emoji);
StickerType get_sticker_type(const td_api::object_ptr<td_api::StickerType> &type);
td_api::object_ptr<td_api::StickerType> get_sticker_type_object(StickerType sticker_type);

View File

@ -8,7 +8,6 @@
#include "td/telegram/AccessRights.h"
#include "td/telegram/AuthManager.h"
#include "td/telegram/AvailableReaction.h"
#include "td/telegram/ConfigManager.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/DialogId.h"
@ -22,6 +21,7 @@
#include "td/telegram/LanguagePackManager.h"
#include "td/telegram/logevent/LogEvent.h"
#include "td/telegram/logevent/LogEventHelper.h"
#include "td/telegram/MessageReaction.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/misc.h"
#include "td/telegram/net/DcId.h"
@ -93,6 +93,82 @@ class GetAvailableReactionsQuery final : public Td::ResultHandler {
}
};
class GetRecentReactionsQuery final : public Td::ResultHandler {
public:
void send(int32 limit, int64 hash) {
send_query(G()->net_query_creator().create(telegram_api::messages_getRecentReactions(limit, hash)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_getRecentReactions>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for GetRecentReactionsQuery: " << to_string(ptr);
td_->stickers_manager_->on_get_recent_reactions(std::move(ptr));
}
void on_error(Status status) final {
LOG(INFO) << "Receive error for GetRecentReactionsQuery: " << status;
td_->stickers_manager_->on_get_recent_reactions(nullptr);
}
};
class GetTopReactionsQuery final : public Td::ResultHandler {
public:
void send(int64 hash) {
send_query(G()->net_query_creator().create(telegram_api::messages_getTopReactions(50, hash)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_getTopReactions>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for GetTopReactionsQuery: " << to_string(ptr);
td_->stickers_manager_->on_get_top_reactions(std::move(ptr));
}
void on_error(Status status) final {
LOG(INFO) << "Receive error for GetTopReactionsQuery: " << status;
td_->stickers_manager_->on_get_top_reactions(nullptr);
}
};
class ClearRecentReactionsQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit ClearRecentReactionsQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send() {
send_query(G()->net_query_creator().create(telegram_api::messages_clearRecentReactions()));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_clearRecentReactions>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
td_->stickers_manager_->reload_recent_reactions();
promise_.set_value(Unit());
}
void on_error(Status status) final {
if (!G()->is_expected_error(status)) {
LOG(ERROR) << "Receive error for clear recent reactions: " << status;
}
td_->stickers_manager_->reload_recent_reactions();
promise_.set_error(std::move(status));
}
};
class GetAllStickersQuery final : public Td::ResultHandler {
StickerType sticker_type_;
@ -918,9 +994,8 @@ class ReadFeaturedStickerSetsQuery final : public Td::ResultHandler {
if (!G()->is_expected_error(status)) {
LOG(ERROR) << "Receive error for ReadFeaturedStickerSetsQuery: " << status;
}
for (int32 type = 0; type < MAX_STICKER_TYPE; type++) {
td_->stickers_manager_->reload_featured_sticker_sets(static_cast<StickerType>(type), true);
}
td_->stickers_manager_->reload_featured_sticker_sets(StickerType::Regular, true);
td_->stickers_manager_->reload_featured_sticker_sets(StickerType::CustomEmoji, true);
}
};
@ -1417,7 +1492,6 @@ void StickersManager::init() {
is_inited_ = true;
{
// add animated emoji sticker set
auto &sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_emoji());
if (G()->is_test_dc()) {
init_special_sticker_set(sticker_set, 1258816259751954, 4879754868529595811, "emojies");
@ -1427,13 +1501,21 @@ void StickersManager::init() {
load_special_sticker_set_info_from_binlog(sticker_set);
}
if (!G()->is_test_dc()) {
// add animated emoji click sticker set
auto &sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_emoji_click());
load_special_sticker_set_info_from_binlog(sticker_set);
}
// add premium gifts sticker set
auto &sticker_set = add_special_sticker_set(SpecialStickerSetType::premium_gifts());
load_special_sticker_set_info_from_binlog(sticker_set);
{
auto &sticker_set = add_special_sticker_set(SpecialStickerSetType::premium_gifts());
load_special_sticker_set_info_from_binlog(sticker_set);
}
{
auto &sticker_set = add_special_sticker_set(SpecialStickerSetType::generic_animations());
load_special_sticker_set_info_from_binlog(sticker_set);
}
{
auto &sticker_set = add_special_sticker_set(SpecialStickerSetType::default_statuses());
load_special_sticker_set_info_from_binlog(sticker_set);
}
dice_emojis_str_ =
td_->option_manager_->get_option_string("dice_emojis", "🎲\x01🎯\x01🏀\x01\x01⚽️\x01🎰\x01🎳");
@ -1444,7 +1526,7 @@ void StickersManager::init() {
}
send_closure(G()->td(), &Td::send_update, get_update_dice_emojis_object());
send_closure_later(actor_id(this), &StickersManager::load_reactions);
load_active_reactions();
on_update_dice_success_values();
on_update_dice_emojis();
@ -1476,15 +1558,95 @@ void StickersManager::init() {
td_->option_manager_->set_option_empty("animated_emoji_sticker_set_name"); // legacy
}
td_api::object_ptr<td_api::emojiReaction> StickersManager::get_emoji_reaction_object(const string &emoji) {
load_reactions();
for (auto &reaction : reactions_.reactions_) {
if (reaction.reaction_ == emoji) {
return td_api::make_object<td_api::emojiReaction>(
reaction.reaction_, reaction.title_, reaction.is_active_, get_sticker_object(reaction.static_icon_),
get_sticker_object(reaction.appear_animation_), get_sticker_object(reaction.select_animation_),
get_sticker_object(reaction.activate_animation_), get_sticker_object(reaction.effect_animation_),
get_sticker_object(reaction.around_animation_), get_sticker_object(reaction.center_animation_));
}
}
return nullptr;
}
vector<string> StickersManager::get_recent_reactions() {
load_recent_reactions();
return recent_reactions_.reactions_;
}
vector<string> StickersManager::get_top_reactions() {
load_top_reactions();
return top_reactions_.reactions_;
}
void StickersManager::add_recent_reaction(const string &reaction) {
load_recent_reactions();
auto &reactions = recent_reactions_.reactions_;
if (!reactions.empty() && reactions[0] == reaction) {
return;
}
auto it = std::find(reactions.begin(), reactions.end(), reaction);
if (it == reactions.end()) {
if (static_cast<int32>(reactions.size()) == MAX_RECENT_REACTIONS) {
reactions.back() = reaction;
} else {
reactions.push_back(reaction);
}
it = reactions.end() - 1;
}
std::rotate(reactions.begin(), it, it + 1);
recent_reactions_.hash_ = get_reactions_hash(reactions);
}
void StickersManager::clear_recent_reactions(Promise<Unit> &&promise) {
load_recent_reactions();
if (recent_reactions_.reactions_.empty()) {
return promise.set_value(Unit());
}
recent_reactions_.hash_ = 0;
recent_reactions_.reactions_.clear();
td_->create_handler<ClearRecentReactionsQuery>(std::move(promise))->send();
}
void StickersManager::reload_reactions() {
if (G()->close_flag() || reactions_.are_being_reloaded_) {
return;
}
CHECK(!td_->auth_manager_->is_bot());
reactions_.are_being_reloaded_ = true;
load_reactions(); // must be after are_being_reloaded_ is set to true to avoid recursion
td_->create_handler<GetAvailableReactionsQuery>()->send(reactions_.hash_);
}
void StickersManager::reload_recent_reactions() {
if (G()->close_flag() || recent_reactions_.is_being_reloaded_) {
return;
}
CHECK(!td_->auth_manager_->is_bot());
recent_reactions_.is_being_reloaded_ = true;
load_recent_reactions(); // must be after is_being_reloaded_ is set to true to avoid recursion
td_->create_handler<GetRecentReactionsQuery>()->send(MAX_RECENT_REACTIONS, recent_reactions_.hash_);
}
void StickersManager::reload_top_reactions() {
if (G()->close_flag() || top_reactions_.is_being_reloaded_) {
return;
}
CHECK(!td_->auth_manager_->is_bot());
top_reactions_.is_being_reloaded_ = true;
load_top_reactions(); // must be after is_being_reloaded_ is set to true to avoid recursion
td_->create_handler<GetTopReactionsQuery>()->send(top_reactions_.hash_);
}
StickersManager::SpecialStickerSet &StickersManager::add_special_sticker_set(const SpecialStickerSetType &type) {
CHECK(!type.is_empty());
auto &result_ptr = special_sticker_sets_[type];
@ -1656,6 +1818,14 @@ void StickersManager::on_load_special_sticker_set(const SpecialStickerSetType &t
try_update_premium_gift_messages();
return;
}
if (type == SpecialStickerSetType::generic_animations()) {
set_promises(pending_get_generic_animations_queries_);
return;
}
if (type == SpecialStickerSetType::default_statuses()) {
set_promises(pending_get_default_statuses_queries_);
return;
}
CHECK(special_sticker_set.id_.is_valid());
auto sticker_set = get_sticker_set(special_sticker_set.id_);
@ -1723,13 +1893,11 @@ StickerType StickersManager::get_sticker_type(FileId file_id) const {
bool StickersManager::is_premium_custom_emoji(int64 custom_emoji_id, bool default_result) const {
auto sticker_id = custom_emoji_to_sticker_id_.get(custom_emoji_id);
const Sticker *s = get_sticker(sticker_id);
if (s == nullptr) {
if (sticker_id.is_valid()) {
LOG(ERROR) << "Failed to find custom emoji sticker " << sticker_id;
}
if (!sticker_id.is_valid()) {
return default_result;
}
const Sticker *s = get_sticker(sticker_id);
CHECK(s != nullptr);
return s->is_premium_;
}
@ -2739,6 +2907,8 @@ StickerSetId StickersManager::get_sticker_set_id(const tl_object_ptr<telegram_ap
case telegram_api::inputStickerSetAnimatedEmoji::ID:
case telegram_api::inputStickerSetAnimatedEmojiAnimations::ID:
case telegram_api::inputStickerSetPremiumGifts::ID:
case telegram_api::inputStickerSetEmojiGenericAnimations::ID:
case telegram_api::inputStickerSetEmojiDefaultStatuses::ID:
LOG(ERROR) << "Receive special sticker set " << to_string(set_ptr);
return add_special_sticker_set(SpecialStickerSetType(set_ptr)).id_;
case telegram_api::inputStickerSetDice::ID:
@ -2769,6 +2939,8 @@ StickerSetId StickersManager::add_sticker_set(tl_object_ptr<telegram_api::InputS
case telegram_api::inputStickerSetAnimatedEmoji::ID:
case telegram_api::inputStickerSetAnimatedEmojiAnimations::ID:
case telegram_api::inputStickerSetPremiumGifts::ID:
case telegram_api::inputStickerSetEmojiGenericAnimations::ID:
case telegram_api::inputStickerSetEmojiDefaultStatuses::ID:
LOG(ERROR) << "Receive special sticker set " << to_string(set_ptr);
return add_special_sticker_set(SpecialStickerSetType(set_ptr)).id_;
case telegram_api::inputStickerSetDice::ID:
@ -2964,6 +3136,8 @@ StickerSetId StickersManager::on_get_input_sticker_set(FileId sticker_file_id,
case telegram_api::inputStickerSetAnimatedEmoji::ID:
case telegram_api::inputStickerSetAnimatedEmojiAnimations::ID:
case telegram_api::inputStickerSetPremiumGifts::ID:
case telegram_api::inputStickerSetEmojiGenericAnimations::ID:
case telegram_api::inputStickerSetEmojiDefaultStatuses::ID:
return add_special_sticker_set(SpecialStickerSetType(set_ptr)).id_;
case telegram_api::inputStickerSetDice::ID:
return StickerSetId();
@ -3637,16 +3811,14 @@ void StickersManager::on_get_special_sticker_set(const SpecialStickerSetType &ty
on_load_special_sticker_set(type, Status::OK());
}
td_api::object_ptr<td_api::updateReactions> StickersManager::get_update_reactions_object() const {
auto reactions = transform(reactions_.reactions_, [this](const Reaction &reaction) {
return td_api::make_object<td_api::reaction>(
reaction.reaction_, reaction.title_, reaction.is_active_, reaction.is_premium_,
get_sticker_object(reaction.static_icon_), get_sticker_object(reaction.appear_animation_),
get_sticker_object(reaction.select_animation_), get_sticker_object(reaction.activate_animation_),
get_sticker_object(reaction.effect_animation_), get_sticker_object(reaction.around_animation_),
get_sticker_object(reaction.center_animation_));
});
return td_api::make_object<td_api::updateReactions>(std::move(reactions));
td_api::object_ptr<td_api::updateActiveEmojiReactions> StickersManager::get_update_active_emoji_reactions_object()
const {
return td_api::make_object<td_api::updateActiveEmojiReactions>(vector<string>(active_reactions_));
}
void StickersManager::save_active_reactions() {
LOG(INFO) << "Save active reactions";
G()->td_db()->get_binlog_pmc()->set("active_reactions", log_event_store(active_reactions_).as_slice().str());
}
void StickersManager::save_reactions() {
@ -3654,40 +3826,124 @@ void StickersManager::save_reactions() {
G()->td_db()->get_binlog_pmc()->set("reactions", log_event_store(reactions_).as_slice().str());
}
void StickersManager::save_recent_reactions() {
LOG(INFO) << "Save recent reactions";
G()->td_db()->get_binlog_pmc()->set("recent_reactions", log_event_store(recent_reactions_).as_slice().str());
}
void StickersManager::save_top_reactions() {
LOG(INFO) << "Save top reactions";
G()->td_db()->get_binlog_pmc()->set("top_reactions", log_event_store(top_reactions_).as_slice().str());
}
void StickersManager::load_active_reactions() {
string active_reactions = G()->td_db()->get_binlog_pmc()->get("active_reactions");
if (active_reactions.empty()) {
return reload_reactions();
}
auto status = log_event_parse(active_reactions_, active_reactions);
if (status.is_error()) {
LOG(ERROR) << "Can't load active reactions: " << status;
active_reactions_ = {};
return reload_reactions();
}
LOG(INFO) << "Successfully loaded " << active_reactions_.size() << " active reactions";
td_->messages_manager_->set_active_reactions(vector<string>(active_reactions_));
send_closure(G()->td(), &Td::send_update, get_update_active_emoji_reactions_object());
}
void StickersManager::load_reactions() {
if (are_reactions_loaded_from_database_) {
return;
}
are_reactions_loaded_from_database_ = true;
string reactions = G()->td_db()->get_binlog_pmc()->get("reactions");
if (reactions.empty()) {
return reload_reactions();
}
auto status = log_event_parse(reactions_, reactions);
auto new_reactions = reactions_;
auto status = log_event_parse(new_reactions, reactions);
if (status.is_error()) {
LOG(ERROR) << "Can't load available reactions: " << status;
reactions_ = {};
return reload_reactions();
}
for (auto &reaction : reactions_.reactions_) {
for (auto &reaction : new_reactions.reactions_) {
if (!reaction.is_valid()) {
LOG(ERROR) << "Loaded invalid reaction";
reactions_ = {};
return reload_reactions();
}
}
reactions_ = std::move(new_reactions);
LOG(INFO) << "Successfully loaded " << reactions_.reactions_.size() << " available reactions";
send_closure(G()->td(), &Td::send_update, get_update_reactions_object());
LOG(INFO) << "Successfully sent updateReactions";
update_active_reactions();
}
void StickersManager::load_recent_reactions() {
if (are_recent_reactions_loaded_from_database_) {
return;
}
are_recent_reactions_loaded_from_database_ = true;
string recent_reactions = G()->td_db()->get_binlog_pmc()->get("recent_reactions");
if (recent_reactions.empty()) {
return reload_recent_reactions();
}
auto status = log_event_parse(recent_reactions_, recent_reactions);
if (status.is_error()) {
LOG(ERROR) << "Can't load recent reactions: " << status;
recent_reactions_ = {};
return reload_recent_reactions();
}
LOG(INFO) << "Successfully loaded " << recent_reactions_.reactions_.size() << " recent reactions";
}
void StickersManager::load_top_reactions() {
if (are_top_reactions_loaded_from_database_) {
return;
}
are_top_reactions_loaded_from_database_ = true;
string top_reactions = G()->td_db()->get_binlog_pmc()->get("top_reactions");
if (top_reactions.empty()) {
return reload_top_reactions();
}
auto status = log_event_parse(top_reactions_, top_reactions);
if (status.is_error()) {
LOG(ERROR) << "Can't load top reactions: " << status;
top_reactions_ = {};
return reload_top_reactions();
}
LOG(INFO) << "Successfully loaded " << top_reactions_.reactions_.size() << " top reactions";
}
void StickersManager::update_active_reactions() {
vector<AvailableReaction> active_reactions;
vector<string> active_reactions;
for (auto &reaction : reactions_.reactions_) {
if (reaction.is_active_) {
active_reactions.emplace_back(reaction.reaction_, reaction.is_premium_);
active_reactions.emplace_back(reaction.reaction_);
}
}
if (active_reactions == active_reactions_) {
return;
}
active_reactions_ = active_reactions;
save_active_reactions();
send_closure(G()->td(), &Td::send_update, get_update_active_emoji_reactions_object());
td_->messages_manager_->set_active_reactions(std::move(active_reactions));
}
@ -3735,18 +3991,89 @@ void StickersManager::on_get_available_reactions(
LOG(ERROR) << "Receive invalid reaction " << reaction.reaction_;
continue;
}
if (reaction.is_premium_) {
LOG(ERROR) << "Receive premium reaction " << reaction.reaction_;
continue;
}
new_reactions.push_back(std::move(reaction));
}
reactions_.reactions_ = std::move(new_reactions);
reactions_.hash_ = available_reactions->hash_;
send_closure(G()->td(), &Td::send_update, get_update_reactions_object());
save_reactions();
update_active_reactions();
}
void StickersManager::on_get_recent_reactions(tl_object_ptr<telegram_api::messages_Reactions> &&reactions_ptr) {
CHECK(recent_reactions_.is_being_reloaded_);
recent_reactions_.is_being_reloaded_ = false;
if (reactions_ptr == nullptr) {
// failed to get recent reactions
return;
}
int32 constructor_id = reactions_ptr->get_id();
if (constructor_id == telegram_api::messages_reactionsNotModified::ID) {
LOG(INFO) << "Top reactions are not modified";
return;
}
CHECK(constructor_id == telegram_api::messages_reactions::ID);
auto reactions = move_tl_object_as<telegram_api::messages_reactions>(reactions_ptr);
auto new_reactions =
transform(reactions->reactions_, [](const telegram_api::object_ptr<telegram_api::Reaction> &reaction) {
return get_message_reaction_string(reaction);
});
if (new_reactions == recent_reactions_.reactions_ && recent_reactions_.hash_ == reactions->hash_) {
LOG(INFO) << "Top reactions are not modified";
return;
}
recent_reactions_.reactions_ = std::move(new_reactions);
recent_reactions_.hash_ = reactions->hash_;
auto expected_hash = get_reactions_hash(recent_reactions_.reactions_);
if (recent_reactions_.hash_ != expected_hash) {
LOG(ERROR) << "Receive hash " << recent_reactions_.hash_ << " instead of " << expected_hash << " for reactions "
<< recent_reactions_.reactions_;
}
save_recent_reactions();
}
void StickersManager::on_get_top_reactions(tl_object_ptr<telegram_api::messages_Reactions> &&reactions_ptr) {
CHECK(top_reactions_.is_being_reloaded_);
top_reactions_.is_being_reloaded_ = false;
if (reactions_ptr == nullptr) {
// failed to get top reactions
return;
}
int32 constructor_id = reactions_ptr->get_id();
if (constructor_id == telegram_api::messages_reactionsNotModified::ID) {
LOG(INFO) << "Top reactions are not modified";
return;
}
CHECK(constructor_id == telegram_api::messages_reactions::ID);
auto reactions = move_tl_object_as<telegram_api::messages_reactions>(reactions_ptr);
auto new_reactions =
transform(reactions->reactions_, [](const telegram_api::object_ptr<telegram_api::Reaction> &reaction) {
return get_message_reaction_string(reaction);
});
if (new_reactions == top_reactions_.reactions_ && top_reactions_.hash_ == reactions->hash_) {
LOG(INFO) << "Top reactions are not modified";
return;
}
top_reactions_.reactions_ = std::move(new_reactions);
top_reactions_.hash_ = reactions->hash_;
save_top_reactions();
}
void StickersManager::on_get_installed_sticker_sets(StickerType sticker_type,
tl_object_ptr<telegram_api::messages_AllStickers> &&stickers_ptr) {
auto type = static_cast<int32>(sticker_type);
@ -5151,13 +5478,11 @@ void StickersManager::on_update_disable_animated_emojis() {
}
}
void StickersManager::on_update_sticker_sets() {
// TODO better support
for (int32 type = 0; type < MAX_STICKER_TYPE; type++) {
archived_sticker_set_ids_[type].clear();
total_archived_sticker_set_count_[type] = -1;
reload_installed_sticker_sets(static_cast<StickerType>(type), true);
}
void StickersManager::on_update_sticker_sets(StickerType sticker_type) {
auto type = static_cast<int32>(sticker_type);
archived_sticker_set_ids_[type].clear();
total_archived_sticker_set_count_[type] = -1;
reload_installed_sticker_sets(sticker_type, true);
}
void StickersManager::try_update_animated_emoji_messages() {
@ -5324,10 +5649,13 @@ void StickersManager::register_emoji(const string &emoji, int64 custom_emoji_id,
}
auto &emoji_messages = *emoji_messages_ptr;
if (emoji_messages.full_message_ids_.empty()) {
emoji_messages.sticker_id_ = get_custom_animated_emoji_sticker_id(custom_emoji_id);
if (!disable_animated_emojis_) {
get_custom_emoji_stickers({custom_emoji_id}, true, Promise<td_api::object_ptr<td_api::stickers>>());
if (!disable_animated_emojis_ && custom_emoji_to_sticker_id_.count(custom_emoji_id) == 0) {
load_custom_emoji_sticker_from_database_force(custom_emoji_id);
if (custom_emoji_to_sticker_id_.count(custom_emoji_id) == 0) {
get_custom_emoji_stickers({custom_emoji_id}, false, Promise<td_api::object_ptr<td_api::stickers>>());
}
}
emoji_messages.sticker_id_ = get_custom_animated_emoji_sticker_id(custom_emoji_id);
}
emoji_messages.full_message_ids_.insert(full_message_id);
return;
@ -5437,6 +5765,107 @@ void StickersManager::get_all_animated_emojis(bool is_recursive,
promise.set_value(td_api::make_object<td_api::emojis>(std::move(emojis)));
}
void StickersManager::get_custom_emoji_reaction_generic_animations(
bool is_recursive, Promise<td_api::object_ptr<td_api::files>> &&promise) {
TRY_STATUS_PROMISE(promise, G()->close_status());
auto &special_sticker_set = add_special_sticker_set(SpecialStickerSetType::generic_animations());
auto sticker_set = get_sticker_set(special_sticker_set.id_);
if (sticker_set == nullptr || !sticker_set->was_loaded_) {
if (is_recursive) {
return promise.set_value(td_api::make_object<td_api::files>());
}
pending_get_generic_animations_queries_.push_back(PromiseCreator::lambda(
[actor_id = actor_id(this), promise = std::move(promise)](Result<Unit> &&result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
send_closure(actor_id, &StickersManager::get_custom_emoji_reaction_generic_animations, true,
std::move(promise));
}
}));
load_special_sticker_set(special_sticker_set);
return;
}
auto files = transform(sticker_set->sticker_ids_,
[&](FileId sticker_id) { return td_->file_manager_->get_file_object(sticker_id); });
promise.set_value(td_api::make_object<td_api::files>(std::move(files)));
}
void StickersManager::get_default_emoji_statuses(bool is_recursive,
Promise<td_api::object_ptr<td_api::emojiStatuses>> &&promise) {
TRY_STATUS_PROMISE(promise, G()->close_status());
auto &special_sticker_set = add_special_sticker_set(SpecialStickerSetType::default_statuses());
auto sticker_set = get_sticker_set(special_sticker_set.id_);
if (sticker_set == nullptr || !sticker_set->was_loaded_) {
if (is_recursive) {
return promise.set_value(td_api::make_object<td_api::emojiStatuses>());
}
pending_get_default_statuses_queries_.push_back(PromiseCreator::lambda(
[actor_id = actor_id(this), promise = std::move(promise)](Result<Unit> &&result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
send_closure(actor_id, &StickersManager::get_default_emoji_statuses, true, std::move(promise));
}
}));
load_special_sticker_set(special_sticker_set);
return;
}
vector<td_api::object_ptr<td_api::emojiStatus>> statuses;
for (auto sticker_id : sticker_set->sticker_ids_) {
auto custom_emoji_id = get_custom_emoji_id(sticker_id);
if (custom_emoji_id == 0) {
LOG(ERROR) << "Ignore wrong sticker " << sticker_id;
continue;
}
statuses.emplace_back(td_api::make_object<td_api::emojiStatus>(custom_emoji_id));
if (statuses.size() >= 8) {
break;
}
}
promise.set_value(td_api::make_object<td_api::emojiStatuses>(std::move(statuses)));
}
bool StickersManager::is_default_emoji_status(int64 custom_emoji_id) {
auto &special_sticker_set = add_special_sticker_set(SpecialStickerSetType::default_statuses());
auto sticker_set = get_sticker_set(special_sticker_set.id_);
if (sticker_set == nullptr || !sticker_set->was_loaded_) {
return false;
}
for (auto sticker_id : sticker_set->sticker_ids_) {
if (get_custom_emoji_id(sticker_id) == custom_emoji_id) {
return true;
}
}
return false;
}
void StickersManager::load_custom_emoji_sticker_from_database_force(int64 custom_emoji_id) {
if (!G()->parameters().use_file_db) {
return;
}
auto value = G()->td_db()->get_sqlite_sync_pmc()->get(get_custom_emoji_database_key(custom_emoji_id));
if (value.empty()) {
LOG(INFO) << "Failed to load custom emoji " << custom_emoji_id << " from database";
return;
}
LOG(INFO) << "Synchronously loaded custom emoji " << custom_emoji_id << " of size " << value.size()
<< " from database";
CustomEmojiLogEvent log_event;
if (log_event_parse(log_event, value).is_error()) {
LOG(ERROR) << "Delete invalid custom emoji " << custom_emoji_id << " value from database";
G()->td_db()->get_sqlite_sync_pmc()->erase(get_custom_emoji_database_key(custom_emoji_id));
}
}
void StickersManager::load_custom_emoji_sticker_from_database(int64 custom_emoji_id, Promise<Unit> &&promise) {
CHECK(custom_emoji_id != 0);
auto &queries = custom_emoji_load_queries_[custom_emoji_id];
@ -5484,13 +5913,17 @@ td_api::object_ptr<td_api::stickers> StickersManager::get_custom_emoji_stickers_
vector<int64> reload_document_ids;
for (auto document_id : document_ids) {
auto file_id = custom_emoji_to_sticker_id_.get(document_id);
auto sticker = get_sticker_object(file_id);
if (sticker != nullptr && sticker->type_->get_id() == td_api::stickerTypeCustomEmoji::ID) {
if (file_id.is_valid()) {
auto s = get_sticker(file_id);
CHECK(s != nullptr);
CHECK(s->type_ == StickerType::CustomEmoji);
if (s->emoji_receive_date_ < update_before_date && !s->is_being_reloaded_) {
s->is_being_reloaded_ = true;
reload_document_ids.push_back(document_id);
}
auto sticker = get_sticker_object(file_id);
CHECK(sticker != nullptr);
stickers.push_back(std::move(sticker));
}
}
@ -6364,6 +6797,7 @@ void StickersManager::on_get_featured_sticker_sets_failed(StickerType sticker_ty
}
void StickersManager::load_featured_sticker_sets(StickerType sticker_type, Promise<Unit> &&promise) {
CHECK(sticker_type != StickerType::Mask);
auto type = static_cast<int32>(sticker_type);
if (td_->auth_manager_->is_bot()) {
are_featured_sticker_sets_loaded_[type] = true;
@ -6667,6 +7101,40 @@ void StickersManager::on_update_sticker_sets_order(StickerType sticker_type,
}
}
// -1 - sticker set can't be moved to top, 0 - order wasn't changed, 1 - sticker set was moved to top
int StickersManager::move_installed_sticker_set_to_top(StickerType sticker_type, StickerSetId sticker_set_id) {
LOG(INFO) << "Move " << sticker_set_id << " to top of " << sticker_type;
auto type = static_cast<int32>(sticker_type);
if (!are_installed_sticker_sets_loaded_[type]) {
return -1;
}
vector<StickerSetId> &current_sticker_set_ids = installed_sticker_set_ids_[type];
auto it = std::find(current_sticker_set_ids.begin(), current_sticker_set_ids.end(), sticker_set_id);
if (it == current_sticker_set_ids.end()) {
return -1;
}
if (sticker_set_id == current_sticker_set_ids[0]) {
CHECK(it == current_sticker_set_ids.begin());
return 0;
}
std::rotate(current_sticker_set_ids.begin(), it, it + 1);
need_update_installed_sticker_sets_[type] = true;
return 1;
}
void StickersManager::on_update_move_sticker_set_to_top(StickerType sticker_type, StickerSetId sticker_set_id) {
int result = move_installed_sticker_set_to_top(sticker_type, sticker_set_id);
if (result < 0) {
return reload_installed_sticker_sets(sticker_type, true);
}
if (result > 0) {
send_update_installed_sticker_sets();
}
}
void StickersManager::reorder_installed_sticker_sets(StickerType sticker_type,
const vector<StickerSetId> &sticker_set_ids,
Promise<Unit> &&promise) {
@ -6682,6 +7150,48 @@ void StickersManager::reorder_installed_sticker_sets(StickerType sticker_type,
promise.set_value(Unit());
}
void StickersManager::move_sticker_set_to_top_by_sticker_id(FileId sticker_id) {
LOG(INFO) << "Move to top sticker set of " << sticker_id;
const auto *s = get_sticker(sticker_id);
if (s == nullptr || !s->set_id_.is_valid()) {
return;
}
if (s->type_ == StickerType::CustomEmoji) {
// just in case
return;
}
if (move_installed_sticker_set_to_top(s->type_, s->set_id_) > 0) {
send_update_installed_sticker_sets();
}
}
void StickersManager::move_sticker_set_to_top_by_custom_emoji_ids(const vector<int64> &custom_emoji_ids) {
LOG(INFO) << "Move to top sticker set of " << custom_emoji_ids;
StickerSetId sticker_set_id;
for (auto custom_emoji_id : custom_emoji_ids) {
auto sticker_id = custom_emoji_to_sticker_id_.get(custom_emoji_id);
if (!sticker_id.is_valid()) {
return;
}
const auto *s = get_sticker(sticker_id);
CHECK(s != nullptr);
CHECK(s->type_ == StickerType::CustomEmoji);
if (!s->set_id_.is_valid()) {
return;
}
if (s->set_id_ != sticker_set_id) {
if (sticker_set_id.is_valid()) {
return;
}
sticker_set_id = s->set_id_;
}
}
CHECK(sticker_set_id.is_valid());
if (move_installed_sticker_set_to_top(StickerType::CustomEmoji, sticker_set_id) > 0) {
send_update_installed_sticker_sets();
}
}
Result<std::tuple<FileId, bool, bool, StickerFormat>> StickersManager::prepare_input_sticker(
td_api::inputSticker *sticker, StickerType sticker_type) {
if (sticker == nullptr) {
@ -6822,7 +7332,7 @@ tl_object_ptr<telegram_api::inputStickerSetItem> StickersManager::get_input_stic
void StickersManager::get_suggested_sticker_set_name(string title, Promise<string> &&promise) {
title = strip_empty_characters(title, MAX_STICKER_SET_TITLE_LENGTH);
if (title.empty()) {
return promise.set_error(Status::Error(400, "Sticker set title can't be empty"));
return promise.set_error(Status::Error(400, "Sticker set title must be non-empty"));
}
td_->create_handler<SuggestStickerSetShortNameQuery>(std::move(promise))->send(title);
@ -6879,12 +7389,12 @@ void StickersManager::create_new_sticker_set(UserId user_id, string title, strin
title = strip_empty_characters(title, MAX_STICKER_SET_TITLE_LENGTH);
if (title.empty()) {
return promise.set_error(Status::Error(400, "Sticker set title can't be empty"));
return promise.set_error(Status::Error(400, "Sticker set title must be non-empty"));
}
short_name = strip_empty_characters(short_name, MAX_STICKER_SET_SHORT_NAME_LENGTH);
if (short_name.empty()) {
return promise.set_error(Status::Error(400, "Sticker set name can't be empty"));
return promise.set_error(Status::Error(400, "Sticker set name must be non-empty"));
}
if (stickers.empty()) {
@ -7119,7 +7629,7 @@ void StickersManager::add_sticker_to_set(UserId user_id, string short_name,
short_name = clean_username(strip_empty_characters(short_name, MAX_STICKER_SET_SHORT_NAME_LENGTH));
if (short_name.empty()) {
return promise.set_error(Status::Error(400, "Sticker set name can't be empty"));
return promise.set_error(Status::Error(400, "Sticker set name must be non-empty"));
}
const StickerSet *sticker_set = get_sticker_set(short_name_to_sticker_set_id_.get(short_name));
@ -7213,7 +7723,7 @@ void StickersManager::set_sticker_set_thumbnail(UserId user_id, string short_nam
short_name = clean_username(strip_empty_characters(short_name, MAX_STICKER_SET_SHORT_NAME_LENGTH));
if (short_name.empty()) {
return promise.set_error(Status::Error(400, "Sticker set name can't be empty"));
return promise.set_error(Status::Error(400, "Sticker set name must be non-empty"));
}
const StickerSet *sticker_set = get_sticker_set(short_name_to_sticker_set_id_.get(short_name));
@ -8812,8 +9322,8 @@ void StickersManager::get_current_state(vector<td_api::object_ptr<td_api::Update
return;
}
if (!reactions_.reactions_.empty()) {
updates.push_back(get_update_reactions_object());
if (!active_reactions_.empty()) {
updates.push_back(get_update_active_emoji_reactions_object());
}
for (int32 type = 0; type < MAX_STICKER_TYPE; type++) {
if (are_installed_sticker_sets_loaded_[type]) {

View File

@ -105,6 +105,13 @@ class StickersManager final : public Actor {
void get_all_animated_emojis(bool is_recursive, Promise<td_api::object_ptr<td_api::emojis>> &&promise);
void get_custom_emoji_reaction_generic_animations(bool is_recursive,
Promise<td_api::object_ptr<td_api::files>> &&promise);
void get_default_emoji_statuses(bool is_recursive, Promise<td_api::object_ptr<td_api::emojiStatuses>> &&promise);
bool is_default_emoji_status(int64 custom_emoji_id);
void get_custom_emoji_stickers(vector<int64> &&document_ids, bool use_database,
Promise<td_api::object_ptr<td_api::stickers>> &&promise);
@ -166,12 +173,30 @@ class StickersManager final : public Actor {
void view_featured_sticker_sets(const vector<StickerSetId> &sticker_set_ids);
td_api::object_ptr<td_api::emojiReaction> get_emoji_reaction_object(const string &emoji);
vector<string> get_recent_reactions();
vector<string> get_top_reactions();
void add_recent_reaction(const string &reaction);
void clear_recent_reactions(Promise<Unit> &&promise);
void reload_reactions();
void reload_recent_reactions();
void reload_top_reactions();
void reload_special_sticker_set_by_type(SpecialStickerSetType type, bool is_recursive = false);
void on_get_available_reactions(tl_object_ptr<telegram_api::messages_AvailableReactions> &&available_reactions_ptr);
void on_get_recent_reactions(tl_object_ptr<telegram_api::messages_Reactions> &&reactions_ptr);
void on_get_top_reactions(tl_object_ptr<telegram_api::messages_Reactions> &&reactions_ptr);
void on_get_installed_sticker_sets(StickerType sticker_type,
tl_object_ptr<telegram_api::messages_AllStickers> &&stickers_ptr);
@ -207,10 +232,12 @@ class StickersManager final : public Actor {
void on_update_emoji_sounds();
void on_update_sticker_sets();
void on_update_sticker_sets(StickerType sticker_type);
void on_update_sticker_sets_order(StickerType sticker_type, const vector<StickerSetId> &sticker_set_ids);
void on_update_move_sticker_set_to_top(StickerType sticker_type, StickerSetId sticker_set_id);
std::pair<int32, vector<StickerSetId>> get_archived_sticker_sets(StickerType sticker_type,
StickerSetId offset_sticker_set_id, int32 limit,
bool force, Promise<Unit> &&promise);
@ -236,6 +263,10 @@ class StickersManager final : public Actor {
void reorder_installed_sticker_sets(StickerType sticker_type, const vector<StickerSetId> &sticker_set_ids,
Promise<Unit> &&promise);
void move_sticker_set_to_top_by_sticker_id(FileId sticker_id);
void move_sticker_set_to_top_by_custom_emoji_ids(const vector<int64> &custom_emoji_ids);
FileId upload_sticker_file(UserId user_id, tl_object_ptr<td_api::inputSticker> &&sticker, Promise<Unit> &&promise);
void get_suggested_sticker_set_name(string title, Promise<string> &&promise);
@ -380,6 +411,8 @@ class StickersManager final : public Actor {
static constexpr int32 EMOJI_KEYWORDS_UPDATE_DELAY = 3600;
static constexpr double MIN_ANIMATED_EMOJI_CLICK_DELAY = 0.2;
static constexpr int32 MAX_RECENT_REACTIONS = 100; // some reasonable value
class Sticker {
public:
StickerSetId set_id_;
@ -538,6 +571,18 @@ class StickersManager final : public Actor {
void parse(ParserT &parser);
};
struct ReactionList {
int64 hash_ = 0;
bool is_being_reloaded_ = false;
vector<string> reactions_;
template <class StorerT>
void store(StorerT &storer) const;
template <class ParserT>
void parse(ParserT &parser);
};
class CustomEmojiLogEvent;
class StickerListLogEvent;
class StickerSetListLogEvent;
@ -570,6 +615,8 @@ class StickersManager final : public Actor {
static string get_custom_emoji_database_key(int64 custom_emoji_id);
void load_custom_emoji_sticker_from_database_force(int64 custom_emoji_id);
void load_custom_emoji_sticker_from_database(int64 custom_emoji_id, Promise<Unit> &&promise);
void on_load_custom_emoji_from_database(int64 custom_emoji_id, string value);
@ -597,6 +644,8 @@ class StickersManager final : public Actor {
int apply_installed_sticker_sets_order(StickerType sticker_type, const vector<StickerSetId> &sticker_set_ids);
int move_installed_sticker_set_to_top(StickerType sticker_type, StickerSetId sticker_set_id);
void on_update_sticker_set(StickerSet *sticker_set, bool is_installed, bool is_archived, bool is_changed,
bool from_database = false);
@ -809,13 +858,25 @@ class StickersManager final : public Actor {
void tear_down() final;
void save_active_reactions();
void save_reactions();
void save_recent_reactions();
void save_top_reactions();
void load_active_reactions();
void load_reactions();
void load_recent_reactions();
void load_top_reactions();
void update_active_reactions();
td_api::object_ptr<td_api::updateReactions> get_update_reactions_object() const;
td_api::object_ptr<td_api::updateActiveEmojiReactions> get_update_active_emoji_reactions_object() const;
SpecialStickerSet &add_special_sticker_set(const SpecialStickerSetType &type);
@ -968,6 +1029,8 @@ class StickersManager final : public Actor {
vector<Promise<Unit>> pending_get_animated_emoji_queries_;
vector<Promise<Unit>> pending_get_premium_gift_option_sticker_queries_;
vector<Promise<Unit>> pending_get_generic_animations_queries_;
vector<Promise<Unit>> pending_get_default_statuses_queries_;
double next_click_animated_emoji_message_time_ = 0;
double next_update_animated_emoji_clicked_time_ = 0;
@ -990,6 +1053,14 @@ class StickersManager final : public Actor {
FlatHashMap<FileId, std::pair<UserId, Promise<Unit>>, FileIdHash> being_uploaded_files_;
Reactions reactions_;
vector<string> active_reactions_;
ReactionList recent_reactions_;
ReactionList top_reactions_;
bool are_reactions_loaded_from_database_ = false;
bool are_recent_reactions_loaded_from_database_ = false;
bool are_top_reactions_loaded_from_database_ = false;
FlatHashMap<string, vector<string>> emoji_language_codes_;
FlatHashMap<string, int32> emoji_language_code_versions_;

View File

@ -113,13 +113,7 @@ FileId StickersManager::parse_sticker(bool in_sticker_set, ParserT &parser) {
} else {
sticker->format_ = StickerFormat::Webp;
}
if (is_emoji) {
sticker->type_ = StickerType::CustomEmoji;
} else if (is_mask) {
sticker->type_ = StickerType::Mask;
} else {
sticker->type_ = StickerType::Regular;
}
sticker->type_ = ::td::get_sticker_type(is_mask, is_emoji);
if (in_sticker_set_stored != in_sticker_set) {
Slice data = parser.template fetch_string_raw<Slice>(parser.get_left_len());
for (auto c : data) {
@ -299,12 +293,7 @@ void StickersManager::parse_sticker_set(StickerSet *sticker_set, ParserT &parser
} else {
sticker_format = StickerFormat::Webp;
}
StickerType sticker_type = StickerType::Regular;
if (is_emojis) {
sticker_type = StickerType::CustomEmoji;
} else if (is_masks) {
sticker_type = StickerType::Mask;
}
auto sticker_type = ::td::get_sticker_type(is_masks, is_emojis);
if (sticker_set->is_inited_) {
string title;
@ -499,6 +488,8 @@ void StickersManager::Reaction::parse(ParserT &parser) {
if (has_center_animation) {
center_animation_ = stickers_manager->parse_sticker(false, parser);
}
is_premium_ = false;
}
template <class StorerT>
@ -525,4 +516,28 @@ void StickersManager::Reactions::parse(ParserT &parser) {
}
}
template <class StorerT>
void StickersManager::ReactionList::store(StorerT &storer) const {
bool has_reactions = !reactions_.empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(has_reactions);
END_STORE_FLAGS();
if (has_reactions) {
td::store(reactions_, storer);
td::store(hash_, storer);
}
}
template <class ParserT>
void StickersManager::ReactionList::parse(ParserT &parser) {
bool has_reactions;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_reactions);
END_PARSE_FLAGS();
if (has_reactions) {
td::parse(reactions_, parser);
td::parse(hash_, parser);
}
}
} // namespace td

View File

@ -41,6 +41,7 @@
#include "td/telegram/DocumentsManager.h"
#include "td/telegram/DownloadManager.h"
#include "td/telegram/DownloadManagerCallback.h"
#include "td/telegram/EmojiStatus.h"
#include "td/telegram/FileReferenceManager.h"
#include "td/telegram/files/FileGcParameters.h"
#include "td/telegram/files/FileId.h"
@ -103,6 +104,7 @@
#include "td/telegram/SecretChatsManager.h"
#include "td/telegram/SecureManager.h"
#include "td/telegram/SecureValue.h"
#include "td/telegram/SentEmailCode.h"
#include "td/telegram/SponsoredMessageManager.h"
#include "td/telegram/StateManager.h"
#include "td/telegram/StickerSetId.h"
@ -2776,11 +2778,11 @@ void Td::set_is_bot_online(bool is_bot_online) {
bool Td::is_authentication_request(int32 id) {
switch (id) {
case td_api::setTdlibParameters::ID:
case td_api::checkDatabaseEncryptionKey::ID:
case td_api::setDatabaseEncryptionKey::ID:
case td_api::getAuthorizationState::ID:
case td_api::setAuthenticationPhoneNumber::ID:
case td_api::setAuthenticationEmailAddress::ID:
case td_api::resendAuthenticationCode::ID:
case td_api::checkAuthenticationEmailCode::ID:
case td_api::checkAuthenticationCode::ID:
case td_api::registerUser::ID:
case td_api::requestQrCodeAuthentication::ID:
@ -2900,8 +2902,6 @@ td_api::object_ptr<td_api::AuthorizationState> Td::get_fake_authorization_state_
switch (state_) {
case State::WaitParameters:
return td_api::make_object<td_api::authorizationStateWaitTdlibParameters>();
case State::Decrypt:
return td_api::make_object<td_api::authorizationStateWaitEncryptionKey>(is_database_encrypted_);
case State::Run:
UNREACHABLE();
return nullptr;
@ -2964,10 +2964,6 @@ void Td::run_request(uint64 id, tl_object_ptr<td_api::Function> function) {
pending_set_parameters_requests_.emplace_back(id, std::move(function));
return;
}
if (init_request_id_ > 0) {
pending_init_requests_.emplace_back(id, std::move(function));
return;
}
int32 function_id = function->get_id();
if (state_ != State::Run) {
@ -2991,18 +2987,22 @@ void Td::run_request(uint64 id, tl_object_ptr<td_api::Function> function) {
case State::WaitParameters: {
switch (function_id) {
case td_api::setTdlibParameters::ID: {
auto status = set_parameters(std::move(move_tl_object_as<td_api::setTdlibParameters>(function)->parameters_));
auto parameters = move_tl_object_as<td_api::setTdlibParameters>(function);
auto database_encryption_key = as_db_key(std::move(parameters->database_encryption_key_), Global::get_use_custom_database(parameters->database_directory_));
auto status = set_parameters(std::move(parameters));
if (status.is_error()) {
return send_closure(actor_id(this), &Td::send_error, id, std::move(status));
}
VLOG(td_init) << "Begin to check parameters";
VLOG(td_init) << "Begin to open database";
set_parameters_request_id_ = id;
auto promise =
PromiseCreator::lambda([actor_id = actor_id(this)](Result<TdDb::CheckedParameters> r_checked_parameters) {
send_closure(actor_id, &Td::on_parameters_checked, std::move(r_checked_parameters));
PromiseCreator::lambda([actor_id = actor_id(this)](Result<TdDb::OpenedDatabase> r_opened_database) {
send_closure(actor_id, &Td::init, std::move(r_opened_database));
});
return TdDb::check_parameters(get_database_scheduler_id(), parameters_, std::move(promise));
return TdDb::open(get_database_scheduler_id(), parameters_, std::move(database_encryption_key),
std::move(promise));
}
default:
if (is_preinitialization_request(function_id)) {
@ -3017,34 +3017,6 @@ void Td::run_request(uint64 id, tl_object_ptr<td_api::Function> function) {
}
break;
}
case State::Decrypt: {
switch (function_id) {
case td_api::checkDatabaseEncryptionKey::ID: {
auto check_key = move_tl_object_as<td_api::checkDatabaseEncryptionKey>(function);
return start_init(id, std::move(check_key->encryption_key_));
}
case td_api::setDatabaseEncryptionKey::ID: {
auto set_key = move_tl_object_as<td_api::setDatabaseEncryptionKey>(function);
return start_init(id, std::move(set_key->new_encryption_key_));
}
case td_api::destroy::ID:
// need to send response synchronously before actual destroying
send_closure(actor_id(this), &Td::send_result, id, td_api::make_object<td_api::ok>());
send_closure(actor_id(this), &Td::destroy);
return;
default:
if (is_preinitialization_request(function_id)) {
break;
}
if (is_preauthentication_request(function_id)) {
pending_preauthentication_requests_.emplace_back(id, std::move(function));
return;
}
return send_error_impl(
id, make_error(400, "Database encryption key is needed: call checkDatabaseEncryptionKey first"));
}
break;
}
case State::Close:
if (destroy_flag_) {
return send_error_impl(id, make_error(401, "Unauthorized"));
@ -3519,11 +3491,8 @@ void Td::close_impl(bool destroy_flag) {
}
LOG(WARNING) << (destroy_flag ? "Destroy" : "Close") << " Td in state " << static_cast<int32>(state_);
if (state_ == State::WaitParameters || state_ == State::Decrypt) {
if (state_ == State::WaitParameters) {
clear_requests();
if (destroy_flag && state_ == State::Decrypt) {
TdDb::destroy(parameters_).ignore();
}
state_ = State::Close;
close_flag_ = 4;
G()->set_close_flag();
@ -3594,29 +3563,6 @@ int32 Td::get_database_scheduler_id() {
return min(current_scheduler_id + 1, scheduler_count - 1);
}
void Td::on_parameters_checked(Result<TdDb::CheckedParameters> r_checked_parameters) {
CHECK(set_parameters_request_id_ != 0);
if (r_checked_parameters.is_error()) {
send_closure(actor_id(this), &Td::send_error, set_parameters_request_id_,
Status::Error(400, r_checked_parameters.error().message()));
return finish_set_parameters();
}
auto checked_parameters = r_checked_parameters.move_as_ok();
parameters_.database_directory = std::move(checked_parameters.database_directory);
parameters_.files_directory = std::move(checked_parameters.files_directory);
is_database_encrypted_ = checked_parameters.is_database_encrypted;
state_ = State::Decrypt;
VLOG(td_init) << "Send authorizationStateWaitEncryptionKey";
send_closure(actor_id(this), &Td::send_update,
td_api::make_object<td_api::updateAuthorizationState>(
td_api::make_object<td_api::authorizationStateWaitEncryptionKey>(is_database_encrypted_)));
VLOG(td_init) << "Finish set parameters";
send_closure(actor_id(this), &Td::send_result, set_parameters_request_id_, td_api::make_object<td_api::ok>());
return finish_set_parameters();
}
void Td::finish_set_parameters() {
CHECK(set_parameters_request_id_ != 0);
set_parameters_request_id_ = 0;
@ -3632,32 +3578,23 @@ void Td::finish_set_parameters() {
}
CHECK(pending_set_parameters_requests_.size() < requests.size());
}
void Td::start_init(uint64 id, string &&key) {
VLOG(td_init) << "Begin to init database";
init_request_id_ = id;
auto promise = PromiseCreator::lambda([actor_id = actor_id(this)](Result<TdDb::OpenedDatabase> r_opened_database) {
send_closure(actor_id, &Td::init, std::move(r_opened_database));
});
TdDb::open(get_database_scheduler_id(), parameters_, as_db_key(std::move(key), parameters_.use_custom_db_format),
std::move(promise));
}
void Td::init(Result<TdDb::OpenedDatabase> r_opened_database) {
CHECK(init_request_id_ != 0);
CHECK(set_parameters_request_id_ != 0);
if (r_opened_database.is_error()) {
LOG(WARNING) << "Failed to open database: " << r_opened_database.error();
send_closure(actor_id(this), &Td::send_error, init_request_id_,
send_closure(actor_id(this), &Td::send_error, set_parameters_request_id_,
Status::Error(400, r_opened_database.error().message()));
return finish_init();
return finish_set_parameters();
}
auto events = r_opened_database.move_as_ok();
parameters_.database_directory = std::move(events.database_directory);
parameters_.files_directory = std::move(events.files_directory);
LOG(INFO) << "Successfully inited database in " << tag("database_directory", parameters_.database_directory)
<< " and " << tag("files_directory", parameters_.files_directory);
VLOG(td_init) << "Successfully inited database";
auto events = r_opened_database.move_as_ok();
G()->init(parameters_, actor_id(this), std::move(events.database)).ensure();
init_options_and_network();
@ -3793,24 +3730,8 @@ void Td::init(Result<TdDb::OpenedDatabase> r_opened_database) {
state_ = State::Run;
send_closure(actor_id(this), &Td::send_result, init_request_id_, td_api::make_object<td_api::ok>());
return finish_init();
}
void Td::finish_init() {
CHECK(init_request_id_ > 0);
init_request_id_ = 0;
if (pending_init_requests_.empty()) {
return;
}
VLOG(td_init) << "Continue to execute " << pending_init_requests_.size() << " pending requests";
auto requests = std::move(pending_init_requests_);
for (auto &request : requests) {
run_request(request.first, std::move(request.second));
}
CHECK(pending_init_requests_.size() < requests.size());
send_closure(actor_id(this), &Td::send_result, set_parameters_request_id_, td_api::make_object<td_api::ok>());
return finish_set_parameters();
}
void Td::init_options_and_network() {
@ -4077,6 +3998,7 @@ void Td::send_update(tl_object_ptr<td_api::Update> &&object) {
case td_api::updateFileAddedToDownloads::ID / 2:
case td_api::updateFileDownload::ID / 2:
case td_api::updateFileRemovedFromDownloads::ID / 2:
case td_api::updateDefaultReactionType::ID / 2:
LOG(ERROR) << "Sending update: " << oneline(to_string(object));
break;
default:
@ -4198,13 +4120,8 @@ Status Td::fix_parameters(TdParameters &parameters) {
return Status::OK();
}
Status Td::set_parameters(td_api::object_ptr<td_api::tdlibParameters> parameters) {
Status Td::set_parameters(td_api::object_ptr<td_api::setTdlibParameters> parameters) {
VLOG(td_init) << "Begin to set TDLib parameters";
if (parameters == nullptr) {
VLOG(td_init) << "Empty parameters";
return Status::Error(400, "Parameters aren't specified");
}
if (!clean_input_string(parameters->api_hash_) || !clean_input_string(parameters->system_language_code_) ||
!clean_input_string(parameters->device_model_) || !clean_input_string(parameters->system_version_) ||
!clean_input_string(parameters->application_version_)) {
@ -4265,10 +4182,6 @@ void Td::on_request(uint64 id, const td_api::setTdlibParameters &request) {
send_error_raw(id, 400, "Unexpected setTdlibParameters");
}
void Td::on_request(uint64 id, const td_api::checkDatabaseEncryptionKey &request) {
send_error_raw(id, 400, "Unexpected checkDatabaseEncryptionKey");
}
void Td::on_request(uint64 id, td_api::setDatabaseEncryptionKey &request) {
CREATE_OK_REQUEST_PROMISE();
G()->td_db()->get_binlog()->change_key(as_db_key(std::move(request.new_encryption_key_), parameters_.use_custom_db_format), std::move(promise));
@ -4284,10 +4197,19 @@ void Td::on_request(uint64 id, td_api::setAuthenticationPhoneNumber &request) {
std::move(request.settings_));
}
void Td::on_request(uint64 id, td_api::setAuthenticationEmailAddress &request) {
CLEAN_INPUT_STRING(request.email_address_);
send_closure(auth_manager_actor_, &AuthManager::set_email_address, id, std::move(request.email_address_));
}
void Td::on_request(uint64 id, const td_api::resendAuthenticationCode &request) {
send_closure(auth_manager_actor_, &AuthManager::resend_authentication_code, id);
}
void Td::on_request(uint64 id, td_api::checkAuthenticationEmailCode &request) {
send_closure(auth_manager_actor_, &AuthManager::check_email_code, id, EmailVerification(std::move(request.code_)));
}
void Td::on_request(uint64 id, td_api::checkAuthenticationCode &request) {
CLEAN_INPUT_STRING(request.code_);
send_closure(auth_manager_actor_, &AuthManager::check_code, id, std::move(request.code_));
@ -4420,6 +4342,41 @@ void Td::on_request(uint64 id, td_api::setPassword &request) {
std::move(request.new_recovery_email_address_), std::move(promise));
}
void Td::on_request(uint64 id, td_api::setLoginEmailAddress &request) {
CHECK_IS_USER();
CLEAN_INPUT_STRING(request.new_login_email_address_);
CREATE_REQUEST_PROMISE();
auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result<SentEmailCode> result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
promise.set_value(result.ok().get_email_address_authentication_code_info_object());
}
});
send_closure(password_manager_, &PasswordManager::set_login_email_address,
std::move(request.new_login_email_address_), std::move(query_promise));
}
void Td::on_request(uint64 id, const td_api::resendLoginEmailAddressCode &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result<SentEmailCode> result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
promise.set_value(result.ok().get_email_address_authentication_code_info_object());
}
});
send_closure(password_manager_, &PasswordManager::resend_login_email_address_code, std::move(query_promise));
}
void Td::on_request(uint64 id, td_api::checkLoginEmailAddressCode &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
send_closure(password_manager_, &PasswordManager::check_login_email_address_code,
EmailVerification(std::move(request.code_)), std::move(promise));
}
void Td::on_request(uint64 id, td_api::setRecoveryEmailAddress &request) {
CHECK_IS_USER();
CLEAN_INPUT_STRING(request.password_);
@ -4454,7 +4411,14 @@ void Td::on_request(uint64 id, const td_api::resendRecoveryEmailAddressCode &req
void Td::on_request(uint64 id, td_api::requestPasswordRecovery &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
send_closure(password_manager_, &PasswordManager::request_password_recovery, std::move(promise));
auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result<SentEmailCode> result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
promise.set_value(result.ok().get_email_address_authentication_code_info_object());
}
});
send_closure(password_manager_, &PasswordManager::request_password_recovery, std::move(query_promise));
}
void Td::on_request(uint64 id, td_api::checkPasswordRecoveryCode &request) {
@ -5272,36 +5236,62 @@ void Td::on_request(uint64 id, const td_api::getChatScheduledMessages &request)
CREATE_REQUEST(GetChatScheduledMessagesRequest, request.chat_id_);
}
void Td::on_request(uint64 id, const td_api::getEmojiReaction &request) {
CHECK_IS_USER();
send_closure(actor_id(this), &Td::send_result, id, stickers_manager_->get_emoji_reaction_object(request.emoji_));
}
void Td::on_request(uint64 id, const td_api::getCustomEmojiReactionAnimations &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
stickers_manager_->get_custom_emoji_reaction_generic_animations(false, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getMessageAvailableReactions &request) {
CHECK_IS_USER();
auto r_reactions =
messages_manager_->get_message_available_reactions({DialogId(request.chat_id_), MessageId(request.message_id_)});
auto r_reactions = messages_manager_->get_message_available_reactions(
{DialogId(request.chat_id_), MessageId(request.message_id_)}, request.row_size_);
if (r_reactions.is_error()) {
send_closure(actor_id(this), &Td::send_error, id, r_reactions.move_as_error());
} else {
auto reactions =
transform(r_reactions.ok(), [](auto &reaction) { return reaction.get_available_reaction_object(); });
send_closure(actor_id(this), &Td::send_result, id,
td_api::make_object<td_api::availableReactions>(std::move(reactions)));
send_closure(actor_id(this), &Td::send_result, id, r_reactions.move_as_ok());
}
}
void Td::on_request(uint64 id, td_api::setMessageReaction &request) {
void Td::on_request(uint64 id, const td_api::clearRecentReactions &request) {
CHECK_IS_USER();
CLEAN_INPUT_STRING(request.reaction_);
CREATE_OK_REQUEST_PROMISE();
messages_manager_->set_message_reaction({DialogId(request.chat_id_), MessageId(request.message_id_)},
std::move(request.reaction_), request.is_big_, std::move(promise));
stickers_manager_->clear_recent_reactions(std::move(promise));
}
void Td::on_request(uint64 id, td_api::addMessageReaction &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
messages_manager_->add_message_reaction({DialogId(request.chat_id_), MessageId(request.message_id_)},
get_message_reaction_string(request.reaction_type_), request.is_big_,
request.update_recent_reactions_, std::move(promise));
}
void Td::on_request(uint64 id, td_api::removeMessageReaction &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
messages_manager_->remove_message_reaction({DialogId(request.chat_id_), MessageId(request.message_id_)},
get_message_reaction_string(request.reaction_type_), std::move(promise));
}
void Td::on_request(uint64 id, td_api::getMessageAddedReactions &request) {
CHECK_IS_USER();
CLEAN_INPUT_STRING(request.reaction_);
CLEAN_INPUT_STRING(request.offset_);
CREATE_REQUEST_PROMISE();
get_message_added_reactions(this, {DialogId(request.chat_id_), MessageId(request.message_id_)},
std::move(request.reaction_), std::move(request.offset_), request.limit_,
std::move(promise));
get_message_reaction_string(request.reaction_type_), std::move(request.offset_),
request.limit_, std::move(promise));
}
void Td::on_request(uint64 id, td_api::setDefaultReactionType &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
set_default_reaction(this, get_message_reaction_string(request.reaction_type_), std::move(promise));
}
void Td::on_request(uint64 id, td_api::getMessagePublicForwards &request) {
@ -6125,9 +6115,6 @@ void Td::on_request(uint64 id, const td_api::toggleBotIsAddedToAttachmentMenu &r
}
void Td::on_request(uint64 id, td_api::setChatAvailableReactions &request) {
for (auto &reaction : request.available_reactions_) {
CLEAN_INPUT_STRING(reaction);
}
CREATE_OK_REQUEST_PROMISE();
messages_manager_->set_dialog_available_reactions(DialogId(request.chat_id_), std::move(request.available_reactions_),
std::move(promise));
@ -6762,6 +6749,36 @@ void Td::on_request(uint64 id, td_api::setUsername &request) {
contacts_manager_->set_username(request.username_, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::setEmojiStatus &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
contacts_manager_->set_emoji_status(EmojiStatus(request.emoji_status_, request.duration_), std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getThemedEmojiStatuses &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
stickers_manager_->get_default_emoji_statuses(false, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getDefaultEmojiStatuses &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
get_default_emoji_statuses(this, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getRecentEmojiStatuses &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
get_recent_emoji_statuses(this, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::clearRecentEmojiStatuses &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
clear_recent_emoji_statuses(this, std::move(promise));
}
void Td::on_request(uint64 id, td_api::setCommands &request) {
CHECK_IS_BOT();
CREATE_OK_REQUEST_PROMISE();
@ -7207,6 +7224,14 @@ void Td::on_request(uint64 id, td_api::reportChatPhoto &request) {
r_report_reason.move_as_ok(), std::move(promise));
}
void Td::on_request(uint64 id, const td_api::reportMessageReactions &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
TRY_RESULT_PROMISE(promise, sender_dialog_id, get_message_sender_dialog_id(this, request.sender_id_, false, false));
report_message_reactions(this, {DialogId(request.chat_id_), MessageId(request.message_id_)}, sender_dialog_id,
std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getChatStatistics &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
@ -7417,6 +7442,7 @@ void Td::on_request(uint64 id, td_api::answerInlineQuery &request) {
void Td::on_request(uint64 id, td_api::getWebAppUrl &request) {
CHECK_IS_USER();
CLEAN_INPUT_STRING(request.url_);
CLEAN_INPUT_STRING(request.application_name_);
CREATE_REQUEST_PROMISE();
auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result<string> result) mutable {
if (result.is_error()) {
@ -7426,7 +7452,8 @@ void Td::on_request(uint64 id, td_api::getWebAppUrl &request) {
}
});
inline_queries_manager_->get_simple_web_view_url(UserId(request.bot_user_id_), std::move(request.url_),
std::move(request.theme_), std::move(query_promise));
std::move(request.theme_), std::move(request.application_name_),
std::move(query_promise));
}
void Td::on_request(uint64 id, td_api::sendWebAppData &request) {
@ -7441,10 +7468,11 @@ void Td::on_request(uint64 id, td_api::sendWebAppData &request) {
void Td::on_request(uint64 id, td_api::openWebApp &request) {
CHECK_IS_USER();
CLEAN_INPUT_STRING(request.url_);
CLEAN_INPUT_STRING(request.application_name_);
CREATE_REQUEST_PROMISE();
attach_menu_manager_->request_web_view(DialogId(request.chat_id_), UserId(request.bot_user_id_),
MessageId(request.reply_to_message_id_), std::move(request.url_),
std::move(request.theme_), std::move(promise));
attach_menu_manager_->request_web_view(
DialogId(request.chat_id_), UserId(request.bot_user_id_), MessageId(request.reply_to_message_id_),
std::move(request.url_), std::move(request.theme_), std::move(request.application_name_), std::move(promise));
}
void Td::on_request(uint64 id, const td_api::closeWebApp &request) {
@ -7640,14 +7668,28 @@ void Td::on_request(uint64 id, td_api::sendEmailAddressVerificationCode &request
CHECK_IS_USER();
CLEAN_INPUT_STRING(request.email_address_);
CREATE_REQUEST_PROMISE();
auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result<SentEmailCode> result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
promise.set_value(result.ok().get_email_address_authentication_code_info_object());
}
});
send_closure(password_manager_, &PasswordManager::send_email_address_verification_code,
std::move(request.email_address_), std::move(promise));
std::move(request.email_address_), std::move(query_promise));
}
void Td::on_request(uint64 id, const td_api::resendEmailAddressVerificationCode &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
send_closure(password_manager_, &PasswordManager::resend_email_address_verification_code, std::move(promise));
auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result<SentEmailCode> result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
promise.set_value(result.ok().get_email_address_authentication_code_info_object());
}
});
send_closure(password_manager_, &PasswordManager::resend_email_address_verification_code, std::move(query_promise));
}
void Td::on_request(uint64 id, td_api::checkEmailAddressVerificationCode &request) {

View File

@ -300,10 +300,8 @@ class Td final : public Actor {
bool destroy_flag_ = false;
int close_flag_ = 0;
enum class State : int32 { WaitParameters, Decrypt, Run, Close } state_ = State::WaitParameters;
uint64 init_request_id_ = 0;
enum class State : int32 { WaitParameters, Run, Close } state_ = State::WaitParameters;
uint64 set_parameters_request_id_ = 0;
bool is_database_encrypted_ = false;
FlatHashMap<uint64, std::shared_ptr<ResultHandler>> result_handlers_;
enum : int8 { RequestActorIdType = 1, ActorIdType = 2 };
@ -399,16 +397,16 @@ class Td final : public Actor {
void on_request(uint64 id, const td_api::setTdlibParameters &request);
void on_request(uint64 id, const td_api::checkDatabaseEncryptionKey &request);
void on_request(uint64 id, td_api::setDatabaseEncryptionKey &request);
void on_request(uint64 id, const td_api::getAuthorizationState &request);
void on_request(uint64 id, td_api::setAuthenticationPhoneNumber &request);
void on_request(uint64 id, td_api::setAuthenticationEmailAddress &request);
void on_request(uint64 id, const td_api::resendAuthenticationCode &request);
void on_request(uint64 id, td_api::checkAuthenticationEmailCode &request);
void on_request(uint64 id, td_api::checkAuthenticationCode &request);
void on_request(uint64 id, td_api::registerUser &request);
@ -433,12 +431,20 @@ class Td final : public Actor {
void on_request(uint64 id, td_api::confirmQrCodeAuthentication &request);
void on_request(uint64 id, td_api::setDatabaseEncryptionKey &request);
void on_request(uint64 id, const td_api::getCurrentState &request);
void on_request(uint64 id, td_api::getPasswordState &request);
void on_request(uint64 id, td_api::setPassword &request);
void on_request(uint64 id, td_api::setLoginEmailAddress &request);
void on_request(uint64 id, const td_api::resendLoginEmailAddressCode &request);
void on_request(uint64 id, td_api::checkLoginEmailAddressCode &request);
void on_request(uint64 id, td_api::getRecoveryEmailAddress &request);
void on_request(uint64 id, td_api::setRecoveryEmailAddress &request);
@ -661,12 +667,22 @@ class Td final : public Actor {
void on_request(uint64 id, const td_api::getChatScheduledMessages &request);
void on_request(uint64 id, const td_api::getEmojiReaction &request);
void on_request(uint64 id, const td_api::getCustomEmojiReactionAnimations &request);
void on_request(uint64 id, const td_api::getMessageAvailableReactions &request);
void on_request(uint64 id, td_api::setMessageReaction &request);
void on_request(uint64 id, const td_api::clearRecentReactions &request);
void on_request(uint64 id, td_api::addMessageReaction &request);
void on_request(uint64 id, td_api::removeMessageReaction &request);
void on_request(uint64 id, td_api::getMessageAddedReactions &request);
void on_request(uint64 id, td_api::setDefaultReactionType &request);
void on_request(uint64 id, td_api::getMessagePublicForwards &request);
void on_request(uint64 id, const td_api::removeNotification &request);
@ -1021,6 +1037,16 @@ class Td final : public Actor {
void on_request(uint64 id, td_api::setUsername &request);
void on_request(uint64 id, const td_api::setEmojiStatus &request);
void on_request(uint64 id, const td_api::getThemedEmojiStatuses &request);
void on_request(uint64 id, const td_api::getDefaultEmojiStatuses &request);
void on_request(uint64 id, const td_api::getRecentEmojiStatuses &request);
void on_request(uint64 id, const td_api::clearRecentEmojiStatuses &request);
void on_request(uint64 id, td_api::setCommands &request);
void on_request(uint64 id, td_api::deleteCommands &request);
@ -1161,6 +1187,8 @@ class Td final : public Actor {
void on_request(uint64 id, td_api::reportChatPhoto &request);
void on_request(uint64 id, const td_api::reportMessageReactions &request);
void on_request(uint64 id, const td_api::getChatStatistics &request);
void on_request(uint64 id, const td_api::getMessageStatistics &request);
@ -1453,12 +1481,8 @@ class Td final : public Actor {
static int32 get_database_scheduler_id();
void on_parameters_checked(Result<TdDb::CheckedParameters> r_checked_parameters);
void finish_set_parameters();
void start_init(uint64 id, string &&key);
void init(Result<TdDb::OpenedDatabase> r_opened_database);
void init_options_and_network();
@ -1469,15 +1493,13 @@ class Td final : public Actor {
void init_managers();
void finish_init();
void clear();
void close_impl(bool destroy_flag);
static Status fix_parameters(TdParameters &parameters) TD_WARN_UNUSED_RESULT;
Status set_parameters(td_api::object_ptr<td_api::tdlibParameters> parameters) TD_WARN_UNUSED_RESULT;
Status set_parameters(td_api::object_ptr<td_api::setTdlibParameters> parameters) TD_WARN_UNUSED_RESULT;
static td_api::object_ptr<td_api::error> make_error(int32 code, CSlice error) {
return td_api::make_object<td_api::error>(code, error.str());

View File

@ -288,7 +288,7 @@ Status TdDb::init_sqlite(const TdParameters &parameters, const DbKey &key, const
bool use_dialog_db = parameters.use_message_db;
bool use_message_db = parameters.use_message_db;
if (!use_sqlite) {
unlink(sql_database_path).ignore();
SqliteDb::destroy(sql_database_path).ignore();
return Status::OK();
}
@ -393,7 +393,11 @@ void TdDb::open(int32 scheduler_id, TdParameters parameters, DbKey key, Promise<
}
void TdDb::open_impl(TdParameters parameters, DbKey key, Promise<OpenedDatabase> &&promise) {
TRY_STATUS_PROMISE(promise, check_parameters(parameters));
OpenedDatabase result;
result.database_directory = parameters.database_directory;
result.files_directory = parameters.files_directory;
// Init pmc
Binlog *binlog_ptr = nullptr;
@ -415,6 +419,11 @@ void TdDb::open_impl(TdParameters parameters, DbKey key, Promise<OpenedDatabase>
config_pmc->external_init_finish(binlog);
VLOG(td_init) << "Finish initialization of config PMC";
if (parameters.use_file_db && binlog_pmc->get("auth").empty()) {
LOG(INFO) << "Destroy SQLite database, because wasn't authorized yet";
SqliteDb::destroy(get_sqlite_path(parameters)).ignore();
}
DbKey new_sqlite_key;
DbKey old_sqlite_key;
bool encrypt_sqlite = encrypt_binlog;
@ -486,16 +495,7 @@ void TdDb::open_impl(TdParameters parameters, DbKey key, Promise<OpenedDatabase>
TdDb::TdDb() = default;
TdDb::~TdDb() = default;
void TdDb::check_parameters(int32 scheduler_id, TdParameters parameters, Promise<CheckedParameters> promise) {
Scheduler::instance()->run_on_scheduler(
scheduler_id, [parameters = std::move(parameters), promise = std::move(promise)](Unit) mutable {
TdDb::check_parameters_impl(std::move(parameters), std::move(promise));
});
}
void TdDb::check_parameters_impl(TdParameters parameters, Promise<CheckedParameters> promise) {
CheckedParameters result;
Status TdDb::check_parameters(TdParameters &parameters) {
auto prepare_dir = [](string dir) -> Result<string> {
CHECK(!dir.empty());
if (dir.back() != TD_DIR_SLASH) {
@ -512,31 +512,20 @@ void TdDb::check_parameters_impl(TdParameters parameters, Promise<CheckedParamet
auto r_database_directory = prepare_dir(parameters.database_directory);
if (r_database_directory.is_error()) {
VLOG(td_init) << "Invalid database_directory";
return promise.set_error(Status::Error(PSLICE()
<< "Can't init database in the directory \"" << parameters.database_directory
<< "\": " << r_database_directory.error()));
return Status::Error(PSLICE() << "Can't init database in the directory \"" << parameters.database_directory
<< "\": " << r_database_directory.error());
}
result.database_directory = r_database_directory.move_as_ok();
parameters.database_directory = result.database_directory;
parameters.database_directory = r_database_directory.move_as_ok();
auto r_files_directory = prepare_dir(parameters.files_directory);
if (r_files_directory.is_error()) {
VLOG(td_init) << "Invalid files_directory";
return promise.set_error(Status::Error(PSLICE() << "Can't init files directory \"" << parameters.files_directory
<< "\": " << r_files_directory.error()));
return Status::Error(PSLICE() << "Can't init files directory \"" << parameters.files_directory
<< "\": " << r_files_directory.error());
}
result.files_directory = r_files_directory.move_as_ok();
parameters.files_directory = r_files_directory.move_as_ok();
Binlog binlog;
auto status = binlog.init(get_binlog_path(parameters), Binlog::Callback());
if (status.is_error() && status.code() != Binlog::Error::WrongPassword) {
LOG(WARNING) << "Failed to check binlog: " << status;
return promise.set_error(std::move(status));
}
result.is_database_encrypted = binlog.get_info().wrong_password;
binlog.close(false /*need_sync*/).ensure();
promise.set_value(std::move(result));
return Status::OK();
}
void TdDb::change_key(DbKey key, Promise<> promise) {

View File

@ -47,14 +47,10 @@ class TdDb {
TdDb &operator=(TdDb &&) = delete;
~TdDb();
struct CheckedParameters {
struct OpenedDatabase {
string database_directory;
string files_directory;
bool is_database_encrypted{false};
};
static void check_parameters(int32 scheduler_id, TdParameters parameters, Promise<CheckedParameters> promise);
struct OpenedDatabase {
unique_ptr<TdDb> database;
vector<BinlogEvent> to_secret_chats_manager;
@ -127,7 +123,7 @@ class TdDb {
static void open_impl(TdParameters parameters, DbKey key, Promise<OpenedDatabase> &&promise);
static void check_parameters_impl(TdParameters parameters, Promise<CheckedParameters> promise);
static Status check_parameters(TdParameters &parameters);
Status init_sqlite(const TdParameters &parameters, const DbKey &key, const DbKey &old_key,
BinlogKeyValue<Binlog> &binlog_pmc);

View File

@ -375,7 +375,7 @@ void TopDialogManager::do_get_top_dialogs(GetTopDialogsQuery &&query) {
}
send_closure(actor_id, &TopDialogManager::on_load_dialogs, std::move(query), r_dialog_ids.move_as_ok());
});
td_->messages_manager_->load_dialogs(std::move(dialog_ids), std::move(promise));
send_closure(td_->messages_manager_actor_, &MessagesManager::load_dialogs, std::move(dialog_ids), std::move(promise));
}
void TopDialogManager::on_load_dialogs(GetTopDialogsQuery &&query, vector<DialogId> &&dialog_ids) {

View File

@ -20,6 +20,7 @@
#include "td/telegram/DialogInviteLink.h"
#include "td/telegram/DialogParticipant.h"
#include "td/telegram/DownloadManager.h"
#include "td/telegram/EmojiStatus.h"
#include "td/telegram/FolderId.h"
#include "td/telegram/Global.h"
#include "td/telegram/GroupCallManager.h"
@ -473,7 +474,7 @@ Promise<> UpdatesManager::set_pts(int32 pts, const char *source) {
last_get_difference_pts_ = get_pts();
schedule_get_difference("rare pts getDifference");
}
} else if (pts < get_pts()) {
} else if (pts < get_pts() && (pts > 1 || td_->option_manager_->get_option_integer("session_count") <= 1)) {
LOG(ERROR) << "Receive wrong pts = " << pts << " from " << source << ". Current pts = " << get_pts();
}
return result;
@ -1063,7 +1064,10 @@ void UpdatesManager::on_get_updates_state(tl_object_ptr<telegram_api::updates_st
LOG(WARNING) << "Restore pts to " << state->pts_;
// restoring right pts
CHECK(pending_pts_updates_.empty());
auto real_running_get_difference = running_get_difference_;
running_get_difference_ = false;
process_postponed_pts_updates(); // drop all updates with old pts
running_get_difference_ = real_running_get_difference;
pts_manager_.init(state->pts_);
last_get_difference_pts_ = get_pts();
last_pts_save_time_ = Time::now() - 2 * MAX_PTS_SAVE_DELAY;
@ -1697,6 +1701,7 @@ void UpdatesManager::try_reload_data() {
td_->animations_manager_->get_saved_animations(Auto());
td_->contacts_manager_->reload_created_public_dialogs(PublicDialogType::HasUsername, Auto());
td_->contacts_manager_->reload_created_public_dialogs(PublicDialogType::IsLocationBased, Auto());
get_default_emoji_statuses(td_, Auto());
td_->notification_settings_manager_->reload_saved_ringtones(Auto());
td_->notification_settings_manager_->send_get_scope_notification_settings_query(NotificationSettingsScope::Private,
Auto());
@ -1705,6 +1710,8 @@ void UpdatesManager::try_reload_data() {
td_->notification_settings_manager_->send_get_scope_notification_settings_query(NotificationSettingsScope::Channel,
Auto());
td_->stickers_manager_->reload_reactions();
td_->stickers_manager_->reload_recent_reactions();
td_->stickers_manager_->reload_top_reactions();
for (int32 type = 0; type < MAX_STICKER_TYPE; type++) {
auto sticker_type = static_cast<StickerType>(type);
td_->stickers_manager_->get_installed_sticker_sets(sticker_type, Auto());
@ -1716,6 +1723,8 @@ void UpdatesManager::try_reload_data() {
td_->stickers_manager_->reload_special_sticker_set_by_type(SpecialStickerSetType::animated_emoji());
td_->stickers_manager_->reload_special_sticker_set_by_type(SpecialStickerSetType::animated_emoji_click());
td_->stickers_manager_->reload_special_sticker_set_by_type(SpecialStickerSetType::premium_gifts());
td_->stickers_manager_->reload_special_sticker_set_by_type(SpecialStickerSetType::generic_animations());
td_->stickers_manager_->reload_special_sticker_set_by_type(SpecialStickerSetType::default_statuses());
schedule_data_reload();
}
@ -2940,6 +2949,11 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateMessageReaction
std::move(promise));
}
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateRecentReactions> update, Promise<Unit> &&promise) {
td_->stickers_manager_->reload_recent_reactions();
promise.set_value(Unit());
}
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateAttachMenuBots> update, Promise<Unit> &&promise) {
td_->attach_menu_manager_->reload_attach_menu_bots(std::move(promise));
}
@ -3165,11 +3179,20 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateUserPhone> upda
}
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateUserPhoto> update, Promise<Unit> &&promise) {
// TODO update->previous_, update->date_
td_->contacts_manager_->on_update_user_photo(UserId(update->user_id_), std::move(update->photo_));
promise.set_value(Unit());
}
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateUserEmojiStatus> update, Promise<Unit> &&promise) {
td_->contacts_manager_->on_update_user_emoji_status(UserId(update->user_id_), std::move(update->emoji_status_));
promise.set_value(Unit());
}
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateRecentEmojiStatuses> update, Promise<Unit> &&promise) {
get_recent_emoji_statuses(td_, Auto());
promise.set_value(Unit());
}
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updatePeerBlocked> update, Promise<Unit> &&promise) {
td_->messages_manager_->on_update_dialog_is_blocked(DialogId(update->peer_id_), update->blocked_);
promise.set_value(Unit());
@ -3352,22 +3375,24 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateNewStickerSet>
}
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateStickerSets> update, Promise<Unit> &&promise) {
td_->stickers_manager_->on_update_sticker_sets();
auto sticker_type = get_sticker_type(update->masks_, update->emojis_);
td_->stickers_manager_->on_update_sticker_sets(sticker_type);
promise.set_value(Unit());
}
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateStickerSetsOrder> update, Promise<Unit> &&promise) {
StickerType sticker_type = StickerType::Regular;
if (update->emojis_) {
sticker_type = StickerType::CustomEmoji;
} else if (update->masks_) {
sticker_type = StickerType::Mask;
}
auto sticker_type = get_sticker_type(update->masks_, update->emojis_);
td_->stickers_manager_->on_update_sticker_sets_order(sticker_type,
StickersManager::convert_sticker_set_ids(update->order_));
promise.set_value(Unit());
}
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateMoveStickerSetToTop> update, Promise<Unit> &&promise) {
auto sticker_type = get_sticker_type(update->masks_, update->emojis_);
td_->stickers_manager_->on_update_move_sticker_set_to_top(sticker_type, StickerSetId(update->stickerset_));
promise.set_value(Unit());
}
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateReadFeaturedStickers> update,
Promise<Unit> &&promise) {
td_->stickers_manager_->reload_featured_sticker_sets(StickerType::Regular, true);

View File

@ -406,6 +406,8 @@ class UpdatesManager final : public Actor {
void on_update(tl_object_ptr<telegram_api::updateMessageReactions> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateRecentReactions> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateAttachMenuBots> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateWebViewResultSent> update, Promise<Unit> &&promise);
@ -420,6 +422,8 @@ class UpdatesManager final : public Actor {
void on_update(tl_object_ptr<telegram_api::updateUserName> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateUserPhone> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateUserPhoto> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateUserEmojiStatus> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateRecentEmojiStatuses> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updatePeerBlocked> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateBotCommands> update, Promise<Unit> &&promise);
@ -489,6 +493,7 @@ class UpdatesManager final : public Actor {
void on_update(tl_object_ptr<telegram_api::updateNewStickerSet> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateStickerSets> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateStickerSetsOrder> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateMoveStickerSetToTop> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateReadFeaturedStickers> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateReadFeaturedEmojiStickers> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateRecentStickers> update, Promise<Unit> &&promise);

View File

@ -10,7 +10,7 @@
namespace td {
constexpr int32 MTPROTO_LAYER = 144;
constexpr int32 MTPROTO_LAYER = 145;
enum class Version : int32 {
Initial, // 0

View File

@ -182,7 +182,7 @@ FileId VoiceNotesManager::on_get_voice_note(unique_ptr<VoiceNote> new_voice_note
v->transcription_id = new_voice_note->transcription_id;
v->text = std::move(new_voice_note->text);
v->last_transcription_error = Status::OK();
on_voice_note_transcription_updated(file_id);
on_voice_note_transcription_completed(file_id);
}
}
@ -316,7 +316,7 @@ void VoiceNotesManager::on_voice_note_transcribed(FileId file_id, string &&text,
auto promises = std::move(it->second);
speech_recognition_queries_.erase(it);
on_voice_note_transcription_updated(file_id);
on_voice_note_transcription_completed(file_id);
set_promises(promises);
} else {
if (is_changed) {
@ -386,6 +386,15 @@ void VoiceNotesManager::on_voice_note_transcription_updated(FileId file_id) {
}
}
void VoiceNotesManager::on_voice_note_transcription_completed(FileId file_id) {
auto it = voice_note_messages_.find(file_id);
if (it != voice_note_messages_.end()) {
for (const auto &full_message_id : it->second) {
td_->messages_manager_->on_update_message_content(full_message_id);
}
}
}
void VoiceNotesManager::rate_speech_recognition(FullMessageId full_message_id, bool is_good, Promise<Unit> &&promise) {
if (!td_->messages_manager_->have_message_force(full_message_id, "rate_speech_recognition")) {
return promise.set_error(Status::Error(400, "Message not found"));

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