tdlight/td/telegram/Td.cpp
Andrea Cavalli b6c5bd07bb Apply tdlight patch
- Memory manager
- TD_SKIP_TEST/TD_SKIP_BENCHMARK/TD_SKIP_TG_CLI
- Rebranding
- github workflow
- .gitignore
- Add crypt32 to windows
- Fatal crash log handler
- optionally unencrypted database
- malloc_trim support
- getMemoryStatistics

Options:
- disable_document_filenames
- disable_minithumbnails
- disable_notifications
- disable_group_calls
- disable_auto_download
- ignore_server_deletes_and_reads
- ignore_update_chat_last_message
- ignore_update_chat_read_inbox
- ignore_update_user_chat_action
- receive_access_hashes
- message_unload_delay modification
2024-08-27 15:24:01 +02:00

1439 lines
62 KiB
C++

//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
//
// Distributed under the 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/Td.h"
#include "td/telegram/AccountManager.h"
#include "td/telegram/AlarmManager.h"
#include "td/telegram/AnimationsManager.h"
#include "td/telegram/Application.h"
#include "td/telegram/AttachMenuManager.h"
#include "td/telegram/AudiosManager.h"
#include "td/telegram/AuthManager.h"
#include "td/telegram/AutosaveManager.h"
#include "td/telegram/BackgroundManager.h"
#include "td/telegram/BoostManager.h"
#include "td/telegram/BotInfoManager.h"
#include "td/telegram/BusinessConnectionManager.h"
#include "td/telegram/BusinessManager.h"
#include "td/telegram/CallbackQueriesManager.h"
#include "td/telegram/CallManager.h"
#include "td/telegram/ChannelRecommendationManager.h"
#include "td/telegram/ChatManager.h"
#include "td/telegram/CommonDialogManager.h"
#include "td/telegram/ConfigManager.h"
#include "td/telegram/ConnectionStateManager.h"
#include "td/telegram/CountryInfoManager.h"
#include "td/telegram/DeviceTokenManager.h"
#include "td/telegram/DialogActionManager.h"
#include "td/telegram/DialogFilterManager.h"
#include "td/telegram/DialogInviteLinkManager.h"
#include "td/telegram/DialogManager.h"
#include "td/telegram/DialogParticipantManager.h"
#include "td/telegram/DocumentsManager.h"
#include "td/telegram/DownloadManager.h"
#include "td/telegram/DownloadManagerCallback.h"
#include "td/telegram/FileReferenceManager.h"
#include "td/telegram/files/FileManager.h"
#include "td/telegram/files/FileSourceId.h"
#include "td/telegram/ForumTopicManager.h"
#include "td/telegram/GameManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/GroupCallManager.h"
#include "td/telegram/HashtagHints.h"
#include "td/telegram/InlineMessageManager.h"
#include "td/telegram/InlineQueriesManager.h"
#include "td/telegram/LanguagePackManager.h"
#include "td/telegram/LinkManager.h"
#include "td/telegram/MessageImportManager.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/misc.h"
#include "td/telegram/net/ConnectionCreator.h"
#include "td/telegram/net/MtprotoHeader.h"
#include "td/telegram/net/NetQuery.h"
#include "td/telegram/net/NetQueryDispatcher.h"
#include "td/telegram/net/NetStatsManager.h"
#include "td/telegram/net/Proxy.h"
#include "td/telegram/net/TempAuthKeyWatchdog.h"
#include "td/telegram/NotificationManager.h"
#include "td/telegram/NotificationSettingsManager.h"
#include "td/telegram/OnlineManager.h"
#include "td/telegram/OptionManager.h"
#include "td/telegram/PasswordManager.h"
#include "td/telegram/PeopleNearbyManager.h"
#include "td/telegram/PhoneNumberManager.h"
#include "td/telegram/PhotoSizeSource.h"
#include "td/telegram/PollManager.h"
#include "td/telegram/PrivacyManager.h"
#include "td/telegram/PromoDataManager.h"
#include "td/telegram/QuickReplyManager.h"
#include "td/telegram/ReactionManager.h"
#include "td/telegram/RequestActor.h"
#include "td/telegram/Requests.h"
#include "td/telegram/SavedMessagesManager.h"
#include "td/telegram/SecretChatsManager.h"
#include "td/telegram/SecureManager.h"
#include "td/telegram/SponsoredMessageManager.h"
#include "td/telegram/StarManager.h"
#include "td/telegram/StateManager.h"
#include "td/telegram/StatisticsManager.h"
#include "td/telegram/StickersManager.h"
#include "td/telegram/StorageManager.h"
#include "td/telegram/MemoryManager.h"
#include "td/telegram/StoryManager.h"
#include "td/telegram/SynchronousRequests.h"
#include "td/telegram/TdDb.h"
#include "td/telegram/TermsOfServiceManager.h"
#include "td/telegram/ThemeManager.h"
#include "td/telegram/TimeZoneManager.h"
#include "td/telegram/TopDialogManager.h"
#include "td/telegram/TranscriptionManager.h"
#include "td/telegram/TranslationManager.h"
#include "td/telegram/UpdatesManager.h"
#include "td/telegram/UserManager.h"
#include "td/telegram/Version.h"
#include "td/telegram/VideoNotesManager.h"
#include "td/telegram/VideosManager.h"
#include "td/telegram/VoiceNotesManager.h"
#include "td/telegram/WebPagesManager.h"
#include "td/db/binlog/BinlogEvent.h"
#include "td/actor/actor.h"
#include "td/utils/misc.h"
#include "td/utils/port/uname.h"
#include "td/utils/Timer.h"
namespace td {
int VERBOSITY_NAME(td_init) = VERBOSITY_NAME(DEBUG) + 3;
int VERBOSITY_NAME(td_requests) = VERBOSITY_NAME(INFO);
void Td::ResultHandler::set_td(Td *td) {
CHECK(td_ == nullptr);
td_ = td;
}
void Td::ResultHandler::send_query(NetQueryPtr query) {
CHECK(!is_query_sent_);
is_query_sent_ = true;
td_->add_handler(query->id(), shared_from_this());
query->debug("Send to NetQueryDispatcher");
G()->net_query_dispatcher().dispatch(std::move(query));
}
Td::Td(unique_ptr<TdCallback> callback, Options options)
: callback_(std::move(callback)), td_options_(std::move(options)) {
CHECK(callback_ != nullptr);
LOG(INFO) << "Create Td with layer " << MTPROTO_LAYER << ", database version " << current_db_version()
<< " and version " << static_cast<int32>(Version::Next) - 1 << " on "
<< Scheduler::instance()->sched_count() << " threads";
}
Td::~Td() = default;
bool Td::ignore_background_updates() const {
return can_ignore_background_updates_ && option_manager_->get_option_boolean("ignore_background_updates");
}
bool Td::is_authentication_request(int32 id) {
switch (id) {
case td_api::setTdlibParameters::ID:
case td_api::getAuthorizationState::ID:
case td_api::setAuthenticationPhoneNumber::ID:
case td_api::sendAuthenticationFirebaseSms::ID:
case td_api::reportAuthenticationCodeMissing::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:
case td_api::resetAuthenticationEmailAddress::ID:
case td_api::checkAuthenticationPassword::ID:
case td_api::requestAuthenticationPasswordRecovery::ID:
case td_api::checkAuthenticationPasswordRecoveryCode::ID:
case td_api::recoverAuthenticationPassword::ID:
case td_api::deleteAccount::ID:
case td_api::logOut::ID:
case td_api::close::ID:
case td_api::destroy::ID:
case td_api::checkAuthenticationBotToken::ID:
return true;
default:
return false;
}
}
bool Td::is_preinitialization_request(int32 id) {
switch (id) {
case td_api::getCurrentState::ID:
case td_api::setAlarm::ID:
case td_api::testUseUpdate::ID:
case td_api::testCallEmpty::ID:
case td_api::testSquareInt::ID:
case td_api::testCallString::ID:
case td_api::testCallBytes::ID:
case td_api::testCallVectorInt::ID:
case td_api::testCallVectorIntObject::ID:
case td_api::testCallVectorString::ID:
case td_api::testCallVectorStringObject::ID:
case td_api::testProxy::ID:
return true;
default:
return false;
}
}
bool Td::is_preauthentication_request(int32 id) {
switch (id) {
case td_api::getInternalLink::ID:
case td_api::getInternalLinkType::ID:
case td_api::getLocalizationTargetInfo::ID:
case td_api::getLanguagePackInfo::ID:
case td_api::getLanguagePackStrings::ID:
case td_api::synchronizeLanguagePack::ID:
case td_api::addCustomServerLanguagePack::ID:
case td_api::setCustomLanguagePack::ID:
case td_api::editCustomLanguagePackInfo::ID:
case td_api::setCustomLanguagePackString::ID:
case td_api::deleteLanguagePack::ID:
case td_api::processPushNotification::ID:
case td_api::getOption::ID:
case td_api::setOption::ID:
case td_api::getStorageStatistics::ID:
case td_api::getStorageStatisticsFast::ID:
case td_api::getDatabaseStatistics::ID:
case td_api::getMemoryStatistics::ID:
case td_api::setNetworkType::ID:
case td_api::getNetworkStatistics::ID:
case td_api::addNetworkStatistics::ID:
case td_api::resetNetworkStatistics::ID:
case td_api::setApplicationVerificationToken::ID:
case td_api::getCountries::ID:
case td_api::getCountryCode::ID:
case td_api::getPhoneNumberInfo::ID:
case td_api::getDeepLinkInfo::ID:
case td_api::getApplicationConfig::ID:
case td_api::saveApplicationLogEvent::ID:
case td_api::addProxy::ID:
case td_api::editProxy::ID:
case td_api::enableProxy::ID:
case td_api::disableProxy::ID:
case td_api::removeProxy::ID:
case td_api::getProxies::ID:
case td_api::getProxyLink::ID:
case td_api::pingProxy::ID:
case td_api::testNetwork::ID:
return true;
default:
return false;
}
}
td_api::object_ptr<td_api::AuthorizationState> Td::get_fake_authorization_state_object() const {
switch (state_) {
case State::WaitParameters:
return td_api::make_object<td_api::authorizationStateWaitTdlibParameters>();
case State::Run:
UNREACHABLE();
return nullptr;
case State::Close:
if (close_flag_ == 5) {
return td_api::make_object<td_api::authorizationStateClosed>();
} else {
return td_api::make_object<td_api::authorizationStateClosing>();
}
default:
UNREACHABLE();
return nullptr;
}
}
vector<td_api::object_ptr<td_api::Update>> Td::get_fake_current_state() const {
CHECK(state_ != State::Run);
vector<td_api::object_ptr<td_api::Update>> updates;
OptionManager::get_common_state(updates);
updates.push_back(td_api::make_object<td_api::updateAuthorizationState>(get_fake_authorization_state_object()));
return updates;
}
void Td::request(uint64 id, tl_object_ptr<td_api::Function> function) {
if (id == 0) {
LOG(ERROR) << "Ignore request with ID == 0: " << to_string(function);
return;
}
if (function == nullptr) {
return callback_->on_error(id, make_error(400, "Request is empty"));
}
VLOG(td_requests) << "Receive request " << id << ": " << to_string(function);
request_set_.emplace(id, function->get_id());
if (SynchronousRequests::is_synchronous_request(function.get())) {
// send response synchronously
return send_result(id, static_request(std::move(function)));
}
run_request(id, std::move(function));
}
void Td::run_request(uint64 id, td_api::object_ptr<td_api::Function> function) {
if (set_parameters_request_id_ > 0) {
pending_set_parameters_requests_.emplace_back(id, std::move(function));
return;
}
int32 function_id = function->get_id();
if (state_ != State::Run) {
switch (function_id) {
case td_api::getAuthorizationState::ID:
// send response synchronously to prevent "Request aborted"
return send_result(id, get_fake_authorization_state_object());
case td_api::getCurrentState::ID:
// send response synchronously to prevent "Request aborted"
return send_result(id, td_api::make_object<td_api::updates>(get_fake_current_state()));
case td_api::close::ID:
// need to send response before actual closing
send_closure(actor_id(this), &Td::send_result, id, td_api::make_object<td_api::ok>());
send_closure(actor_id(this), &Td::close);
return;
default:
break;
}
}
switch (state_) {
case State::WaitParameters: {
switch (function_id) {
case td_api::setTdlibParameters::ID: {
auto r_parameters = get_parameters(move_tl_object_as<td_api::setTdlibParameters>(function));
if (r_parameters.is_error()) {
return send_closure(actor_id(this), &Td::send_error, id, r_parameters.move_as_error());
}
auto parameters = r_parameters.move_as_ok();
VLOG(td_init) << "Begin to open database";
set_parameters_request_id_ = id;
can_ignore_background_updates_ = !parameters.second.use_chat_info_database_ &&
!parameters.second.use_message_database_ &&
!parameters.first.use_secret_chats_;
auto promise = PromiseCreator::lambda(
[actor_id = actor_id(this), parameters = std::move(parameters.first),
parent = create_reference()](Result<TdDb::OpenedDatabase> r_opened_database) mutable {
send_closure(actor_id, &Td::init, std::move(parameters), std::move(r_opened_database));
});
auto use_sqlite_pmc = parameters.second.use_message_database_ || parameters.second.use_chat_info_database_ ||
parameters.second.use_file_database_;
return TdDb::open(use_sqlite_pmc ? G()->get_database_scheduler_id() : G()->get_slow_net_scheduler_id(),
std::move(parameters.second), std::move(promise));
}
default:
if (is_preinitialization_request(function_id)) {
return requests_->run_request(id, std::move(function));
}
if (is_preauthentication_request(function_id)) {
pending_preauthentication_requests_.emplace_back(id, std::move(function));
return;
}
return send_error_impl(
id, make_error(400, "Initialization parameters are needed: call setTdlibParameters first"));
}
UNREACHABLE();
}
case State::Close:
return send_error_impl(id, make_error(destroy_flag_ ? 401 : 500,
destroy_flag_ ? CSlice("Unauthorized") : CSlice("Request aborted")));
case State::Run:
if (!auth_manager_->is_authorized() && !is_preauthentication_request(function_id) &&
!is_preinitialization_request(function_id) && !is_authentication_request(function_id)) {
return send_error_impl(id, make_error(401, "Unauthorized"));
}
return requests_->run_request(id, std::move(function));
default:
UNREACHABLE();
}
}
td_api::object_ptr<td_api::Object> Td::static_request(td_api::object_ptr<td_api::Function> function) {
return SynchronousRequests::run_request(std::move(function));
}
void Td::add_handler(uint64 id, std::shared_ptr<ResultHandler> handler) {
result_handlers_[id] = std::move(handler);
}
std::shared_ptr<Td::ResultHandler> Td::extract_handler(uint64 id) {
auto it = result_handlers_.find(id);
if (it == result_handlers_.end()) {
return nullptr;
}
auto result = std::move(it->second);
result_handlers_.erase(it);
return result;
}
void Td::on_update(telegram_api::object_ptr<telegram_api::Updates> updates, uint64 auth_key_id) {
if (close_flag_ > 1) {
return;
}
if (updates == nullptr) {
if (auth_manager_->is_bot()) {
G()->net_query_dispatcher().update_mtproto_header();
} else {
// this could be a min-channel update
updates_manager_->schedule_get_difference("failed to fetch updates");
}
} else {
updates_manager_->on_update_from_auth_key_id(auth_key_id);
updates_manager_->on_get_updates(std::move(updates), Promise<Unit>());
if (auth_manager_->is_bot() && auth_manager_->is_authorized()) {
online_manager_->set_is_bot_online(true);
}
}
}
void Td::on_result(NetQueryPtr query) {
query->debug("Td: received from DcManager");
VLOG(net_query) << "Receive result of " << query;
if (close_flag_ > 1) {
return;
}
auto handler = extract_handler(query->id());
if (handler != nullptr) {
CHECK(query->is_ready());
if (query->is_ok()) {
handler->on_result(query->move_as_ok());
} else {
handler->on_error(query->move_as_error());
}
} else {
if (!query->is_ok() || query->ok_tl_constructor() != telegram_api::upload_file::ID) {
LOG(WARNING) << query << " is ignored: no handlers found";
}
query->clear();
}
}
void Td::start_up() {
uint64 check_endianness = 0x0706050403020100;
auto check_endianness_raw = reinterpret_cast<const unsigned char *>(&check_endianness);
for (unsigned char c = 0; c < 8; c++) {
auto symbol = check_endianness_raw[static_cast<size_t>(c)];
LOG_IF(FATAL, symbol != c) << "TDLib requires little-endian platform";
}
requests_ = make_unique<Requests>(this);
VLOG(td_init) << "Create Global";
old_context_ = set_context(std::make_shared<Global>());
G()->set_net_query_stats(td_options_.net_query_stats);
inc_request_actor_refcnt(); // guard
inc_actor_refcnt(); // guard
alarm_manager_ = create_actor<AlarmManager>("AlarmManager", create_reference());
CHECK(state_ == State::WaitParameters);
for (auto &update : get_fake_current_state()) {
send_update(std::move(update));
}
}
void Td::tear_down() {
LOG_CHECK(close_flag_ == 5) << close_flag_;
}
void Td::hangup_shared() {
auto token = get_link_token();
auto type = Container<int>::type_from_id(token);
if (type == RequestActorIdType) {
request_actors_.erase(token);
dec_request_actor_refcnt();
} else if (type == ActorIdType) {
dec_actor_refcnt();
} else {
LOG(FATAL) << "Unknown hangup_shared of type " << type;
}
}
void Td::hangup() {
LOG(INFO) << "Receive Td::hangup";
close();
dec_stop_cnt();
}
ActorShared<Td> Td::create_reference() {
inc_actor_refcnt();
return actor_shared(this, ActorIdType);
}
void Td::inc_actor_refcnt() {
actor_refcnt_++;
}
void Td::dec_actor_refcnt() {
actor_refcnt_--;
if (actor_refcnt_ < 3) {
LOG(DEBUG) << "Decrease reference count to " << actor_refcnt_;
}
if (actor_refcnt_ == 0) {
if (close_flag_ == 2) {
create_reference();
close_flag_ = 3;
} else if (close_flag_ == 3) {
LOG(INFO) << "All actors were closed";
Timer timer;
auto reset_manager = [&timer](auto &manager, Slice name) {
manager.reset();
LOG(DEBUG) << name << " was cleared" << timer;
};
reset_manager(account_manager_, "AccountManager");
reset_manager(animations_manager_, "AnimationsManager");
reset_manager(attach_menu_manager_, "AttachMenuManager");
reset_manager(audios_manager_, "AudiosManager");
reset_manager(auth_manager_, "AuthManager");
reset_manager(autosave_manager_, "AutosaveManager");
reset_manager(background_manager_, "BackgroundManager");
reset_manager(boost_manager_, "BoostManager");
reset_manager(bot_info_manager_, "BotInfoManager");
reset_manager(business_connection_manager_, "BusinessConnectionManager");
reset_manager(business_manager_, "BusinessManager");
reset_manager(callback_queries_manager_, "CallbackQueriesManager");
reset_manager(channel_recommendation_manager_, "ChannelRecommendationManager");
reset_manager(chat_manager_, "ChatManager");
reset_manager(common_dialog_manager_, "CommonDialogManager");
reset_manager(connection_state_manager_, "ConnectionStateManager");
reset_manager(country_info_manager_, "CountryInfoManager");
reset_manager(dialog_action_manager_, "DialogActionManager");
reset_manager(dialog_filter_manager_, "DialogFilterManager");
reset_manager(dialog_invite_link_manager_, "DialogInviteLinkManager");
reset_manager(dialog_manager_, "DialogManager");
reset_manager(dialog_participant_manager_, "DialogParticipantManager");
reset_manager(documents_manager_, "DocumentsManager");
reset_manager(download_manager_, "DownloadManager");
reset_manager(file_manager_, "FileManager");
reset_manager(file_reference_manager_, "FileReferenceManager");
reset_manager(forum_topic_manager_, "ForumTopicManager");
reset_manager(game_manager_, "GameManager");
reset_manager(group_call_manager_, "GroupCallManager");
reset_manager(inline_message_manager_, "InlineMessageManager");
reset_manager(inline_queries_manager_, "InlineQueriesManager");
reset_manager(link_manager_, "LinkManager");
reset_manager(message_import_manager_, "MessageImportManager");
reset_manager(messages_manager_, "MessagesManager");
reset_manager(notification_manager_, "NotificationManager");
reset_manager(notification_settings_manager_, "NotificationSettingsManager");
reset_manager(online_manager_, "OnlineManager");
reset_manager(people_nearby_manager_, "PeopleNearbyManager");
reset_manager(phone_number_manager_, "PhoneNumberManager");
reset_manager(poll_manager_, "PollManager");
reset_manager(privacy_manager_, "PrivacyManager");
reset_manager(promo_data_manager_, "PromoDataManager");
reset_manager(quick_reply_manager_, "QuickReplyManager");
reset_manager(reaction_manager_, "ReactionManager");
reset_manager(saved_messages_manager_, "SavedMessagesManager");
reset_manager(sponsored_message_manager_, "SponsoredMessageManager");
reset_manager(star_manager_, "StarManager");
reset_manager(statistics_manager_, "StatisticsManager");
reset_manager(stickers_manager_, "StickersManager");
reset_manager(memory_manager_, "MemoryManager");
reset_manager(story_manager_, "StoryManager");
reset_manager(terms_of_service_manager_, "TermsOfServiceManager");
reset_manager(theme_manager_, "ThemeManager");
reset_manager(time_zone_manager_, "TimeZoneManager");
reset_manager(top_dialog_manager_, "TopDialogManager");
reset_manager(transcription_manager_, "TranscriptionManager");
reset_manager(translation_manager_, "TranslationManager");
reset_manager(updates_manager_, "UpdatesManager");
reset_manager(user_manager_, "UserManager");
reset_manager(video_notes_manager_, "VideoNotesManager");
reset_manager(videos_manager_, "VideosManager");
reset_manager(voice_notes_manager_, "VoiceNotesManager");
reset_manager(web_pages_manager_, "WebPagesManager");
G()->set_option_manager(nullptr);
option_manager_.reset();
LOG(DEBUG) << "OptionManager was cleared" << timer;
G()->close_all(destroy_flag_,
PromiseCreator::lambda([actor_id = create_reference()](Unit) mutable { actor_id.reset(); }));
// NetQueryDispatcher will be closed automatically
close_flag_ = 4;
} else if (close_flag_ == 4) {
on_closed();
#ifdef __linux__
#if defined(__GLIBC__) && !defined(__UCLIBC__) && !defined(__MUSL__)
malloc_trim(0);
#endif
#endif
} else {
UNREACHABLE();
}
} else {
#ifdef __linux__
#if defined(__GLIBC__) && !defined(__UCLIBC__) && !defined(__MUSL__)
malloc_trim(0);
#endif
#endif
}
}
void Td::on_closed() {
close_flag_ = 5;
send_update(
td_api::make_object<td_api::updateAuthorizationState>(td_api::make_object<td_api::authorizationStateClosed>()));
dec_stop_cnt();
}
void Td::dec_stop_cnt() {
stop_cnt_--;
if (stop_cnt_ == 0) {
LOG(INFO) << "Stop Td";
set_context(std::move(old_context_));
stop();
}
}
void Td::inc_request_actor_refcnt() {
request_actor_refcnt_++;
}
void Td::dec_request_actor_refcnt() {
request_actor_refcnt_--;
LOG(DEBUG) << "Decrease request actor count to " << request_actor_refcnt_;
if (request_actor_refcnt_ == 0) {
clear();
dec_actor_refcnt(); // remove guard
}
}
void Td::clear_requests() {
while (!request_set_.empty()) {
uint64 id = request_set_.begin()->first;
if (destroy_flag_) {
send_error_impl(id, make_error(401, "Unauthorized"));
} else {
send_error_impl(id, make_error(500, "Request aborted"));
}
}
}
void Td::clear() {
if (close_flag_ >= 2) {
return;
}
LOG(INFO) << "Clear Td";
close_flag_ = 2;
Timer timer;
if (!auth_manager_->is_bot()) {
if (destroy_flag_) {
notification_manager_->destroy_all_notifications();
} else {
notification_manager_->flush_all_notifications();
}
}
G()->net_query_creator().stop_check();
result_handlers_.clear();
LOG(DEBUG) << "Handlers were cleared" << timer;
G()->net_query_dispatcher().stop();
LOG(DEBUG) << "NetQueryDispatcher was stopped" << timer;
state_manager_.reset();
LOG(DEBUG) << "StateManager was cleared" << timer;
clear_requests();
auto reset_actor = [&timer](ActorOwn<Actor> actor) {
if (!actor.empty()) {
LOG(DEBUG) << "Start clearing " << actor.get().get_name() << timer;
}
};
// close all pure actors
reset_actor(ActorOwn<Actor>(std::move(alarm_manager_)));
reset_actor(ActorOwn<Actor>(std::move(call_manager_)));
reset_actor(ActorOwn<Actor>(std::move(cashtag_search_hints_)));
reset_actor(ActorOwn<Actor>(std::move(config_manager_)));
reset_actor(ActorOwn<Actor>(std::move(device_token_manager_)));
reset_actor(ActorOwn<Actor>(std::move(hashtag_hints_)));
reset_actor(ActorOwn<Actor>(std::move(hashtag_search_hints_)));
reset_actor(ActorOwn<Actor>(std::move(language_pack_manager_)));
reset_actor(ActorOwn<Actor>(std::move(net_stats_manager_)));
reset_actor(ActorOwn<Actor>(std::move(password_manager_)));
reset_actor(ActorOwn<Actor>(std::move(secure_manager_)));
reset_actor(ActorOwn<Actor>(std::move(secret_chats_manager_)));
reset_actor(ActorOwn<Actor>(std::move(storage_manager_)));
G()->set_connection_creator(ActorOwn<ConnectionCreator>());
LOG(DEBUG) << "ConnectionCreator was cleared" << timer;
G()->set_temp_auth_key_watchdog(ActorOwn<TempAuthKeyWatchdog>());
LOG(DEBUG) << "TempAuthKeyWatchdog was cleared" << timer;
// clear actors which are unique pointers
reset_actor(ActorOwn<Actor>(std::move(account_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(animations_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(attach_menu_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(auth_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(autosave_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(background_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(boost_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(bot_info_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(business_connection_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(business_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(channel_recommendation_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(chat_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(common_dialog_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(connection_state_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(country_info_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(dialog_action_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(dialog_filter_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(dialog_invite_link_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(dialog_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(dialog_participant_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(download_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(file_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(file_reference_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(forum_topic_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(game_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(group_call_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(inline_message_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(inline_queries_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(link_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(message_import_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(messages_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(notification_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(notification_settings_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(online_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(people_nearby_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(phone_number_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(poll_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(privacy_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(promo_data_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(quick_reply_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(reaction_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(saved_messages_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(sponsored_message_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(star_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(statistics_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(stickers_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(memory_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(story_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(terms_of_service_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(theme_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(time_zone_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(top_dialog_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(transcription_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(translation_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(updates_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(user_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(video_notes_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(voice_notes_manager_actor_)));
reset_actor(ActorOwn<Actor>(std::move(web_pages_manager_actor_)));
LOG(DEBUG) << "All actors were cleared" << timer;
}
void Td::close() {
close_impl(false);
}
void Td::destroy() {
close_impl(true);
}
void Td::close_impl(bool destroy_flag) {
destroy_flag_ |= destroy_flag;
if (close_flag_) {
return;
}
LOG(WARNING) << (destroy_flag ? "Destroy" : "Close") << " Td in state " << static_cast<int32>(state_);
if (state_ == State::WaitParameters) {
state_ = State::Close;
close_flag_ = 4;
G()->set_close_flag();
clear_requests();
alarm_manager_.reset();
send_update(td_api::make_object<td_api::updateAuthorizationState>(
td_api::make_object<td_api::authorizationStateClosing>()));
request_actors_.clear();
return send_closure_later(actor_id(this), &Td::dec_request_actor_refcnt); // remove guard
}
state_ = State::Close;
close_flag_ = 1;
G()->set_close_flag();
send_closure(auth_manager_actor_, &AuthManager::on_closing, destroy_flag);
updates_manager_->timeout_expired(); // save PTS and QTS
// wait till all request_actors will stop
request_actors_.clear();
G()->td_db()->flush_all();
send_closure_later(actor_id(this), &Td::dec_request_actor_refcnt); // remove guard
}
template <class T>
void Td::complete_pending_preauthentication_requests(const T &func) {
for (auto &request : pending_preauthentication_requests_) {
if (request.second != nullptr && func(request.second->get_id())) {
requests_->run_request(request.first, std::move(request.second));
request.second = nullptr;
}
}
}
void Td::finish_set_parameters() {
CHECK(set_parameters_request_id_ != 0);
set_parameters_request_id_ = 0;
if (pending_set_parameters_requests_.empty()) {
return;
}
VLOG(td_init) << "Continue to execute " << pending_set_parameters_requests_.size() << " pending requests";
auto requests = std::move(pending_set_parameters_requests_);
for (auto &request : requests) {
run_request(request.first, std::move(request.second));
}
CHECK(pending_set_parameters_requests_.size() < requests.size());
}
void Td::init(Parameters parameters, Result<TdDb::OpenedDatabase> r_opened_database) {
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, set_parameters_request_id_, r_opened_database.move_as_error());
return finish_set_parameters();
}
auto events = r_opened_database.move_as_ok();
VLOG(td_init) << "Successfully inited database";
if (state_ == State::Close) {
LOG(INFO) << "Close asynchronously opened database";
auto database_ptr = events.database.get();
auto promise = PromiseCreator::lambda([database = std::move(events.database)](Unit) {
// destroy the database after closing
});
database_ptr->close(
database_ptr->use_file_database() ? G()->get_database_scheduler_id() : G()->get_slow_net_scheduler_id(),
destroy_flag_, std::move(promise));
return finish_set_parameters();
}
G()->init(actor_id(this), std::move(events.database)).ensure();
init_options_and_network();
// we need to process td_api::getOption along with td_api::setOption for consistency
// we need to process td_api::setOption before managers and MTProto header are created,
// because their initialiation may be affected by the options
complete_pending_preauthentication_requests([](int32 id) {
switch (id) {
case td_api::getOption::ID:
case td_api::setOption::ID:
return true;
default:
return false;
}
});
if (!option_manager_->get_option_boolean("disable_network_statistics")) {
net_stats_manager_ = create_actor<NetStatsManager>("NetStatsManager", create_reference());
// How else could I let two actor know about each other, without quite complex async logic?
auto net_stats_manager_ptr = net_stats_manager_.get_actor_unsafe();
net_stats_manager_ptr->init();
G()->connection_creator().get_actor_unsafe()->set_net_stats_callback(
net_stats_manager_ptr->get_common_stats_callback(), net_stats_manager_ptr->get_media_stats_callback());
G()->set_net_stats_file_callbacks(net_stats_manager_ptr->get_file_stats_callbacks());
}
complete_pending_preauthentication_requests([](int32 id) {
switch (id) {
case td_api::getNetworkStatistics::ID:
case td_api::addNetworkStatistics::ID:
case td_api::resetNetworkStatistics::ID:
return true;
default:
return false;
}
});
if (events.since_last_open >= 3600) {
auto old_since_last_open = option_manager_->get_option_integer("since_last_open");
if (events.since_last_open > old_since_last_open) {
option_manager_->set_option_integer("since_last_open", events.since_last_open);
}
}
options_.language_pack = option_manager_->get_option_string("localization_target");
options_.language_code = option_manager_->get_option_string("language_pack_id");
options_.parameters = option_manager_->get_option_string("connection_parameters");
options_.tz_offset = static_cast<int32>(option_manager_->get_option_integer("utc_time_offset"));
options_.is_emulator = option_manager_->get_option_boolean("is_emulator");
// options_.proxy = Proxy();
G()->set_mtproto_header(make_unique<MtprotoHeader>(options_));
G()->set_store_all_files_in_files_directory(
option_manager_->get_option_boolean("store_all_files_in_files_directory"));
VLOG(td_init) << "Create NetQueryDispatcher";
auto net_query_dispatcher = make_unique<NetQueryDispatcher>([&] { return create_reference(); });
G()->set_net_query_dispatcher(std::move(net_query_dispatcher));
complete_pending_preauthentication_requests([](int32 id) {
// pingProxy uses NetQueryDispatcher to get main_dc_id, so must be called after NetQueryDispatcher is created
return id == td_api::pingProxy::ID;
});
VLOG(td_init) << "Create AuthManager";
auth_manager_ = td::make_unique<AuthManager>(parameters.api_id_, parameters.api_hash_, create_reference());
auth_manager_actor_ = register_actor("AuthManager", auth_manager_.get());
G()->set_auth_manager(auth_manager_actor_.get());
init_file_manager();
init_non_actor_managers();
init_managers();
init_pure_actor_managers();
secret_chats_manager_ =
create_actor<SecretChatsManager>("SecretChatsManager", create_reference(), parameters.use_secret_chats_);
G()->set_secret_chats_manager(secret_chats_manager_.get());
storage_manager_ = create_actor<StorageManager>("StorageManager", create_reference(), G()->get_gc_scheduler_id());
G()->set_storage_manager(storage_manager_.get());
option_manager_->on_td_inited();
process_binlog_events(std::move(events));
VLOG(td_init) << "Ping datacenter";
if (!auth_manager_->is_authorized()) {
country_info_manager_->get_current_country_code(Promise<string>());
} else {
updates_manager_->get_difference("init");
}
complete_pending_preauthentication_requests([](int32 id) { return true; });
VLOG(td_init) << "Finish initialization";
state_ = State::Run;
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::process_binlog_events(TdDb::OpenedDatabase &&events) {
VLOG(td_init) << "Send binlog events";
for (auto &event : events.user_events) {
user_manager_->on_binlog_user_event(std::move(event));
}
for (auto &event : events.channel_events) {
chat_manager_->on_binlog_channel_event(std::move(event));
}
// chats may contain links to channels, so should be inited after
for (auto &event : events.chat_events) {
chat_manager_->on_binlog_chat_event(std::move(event));
}
for (auto &event : events.secret_chat_events) {
user_manager_->on_binlog_secret_chat_event(std::move(event));
}
for (auto &event : events.web_page_events) {
web_pages_manager_->on_binlog_web_page_event(std::move(event));
}
for (auto &event : events.save_app_log_events) {
on_save_app_log_binlog_event(this, std::move(event));
}
// Send binlog events to managers
//
// 1. Actors must receive all binlog events before other queries.
//
// -- All actors have one "entry point". So there is only one way to send query to them. So all queries are ordered
// for each Actor.
//
// 2. An actor must not make some decisions before all binlog events are processed.
// For example, SecretChatActor must not send RequestKey, before it receives log event with RequestKey and understands
// that RequestKey was already sent.
//
// 3. During replay of binlog some queries may be sent to other actors. They shouldn't process such events before all
// their binlog events are processed. So actor may receive some old queries. It must be in its actual state in
// order to handle them properly.
//
// -- Use send_closure_later, so actors don't even start process binlog events, before all binlog events are sent
for (auto &event : events.to_secret_chats_manager) {
send_closure_later(secret_chats_manager_, &SecretChatsManager::replay_binlog_event, std::move(event));
}
send_closure_later(account_manager_actor_, &AccountManager::on_binlog_events, std::move(events.to_account_manager));
send_closure_later(poll_manager_actor_, &PollManager::on_binlog_events, std::move(events.to_poll_manager));
send_closure_later(messages_manager_actor_, &MessagesManager::on_binlog_events,
std::move(events.to_messages_manager));
send_closure_later(story_manager_actor_, &StoryManager::on_binlog_events, std::move(events.to_story_manager));
send_closure_later(notification_manager_actor_, &NotificationManager::on_binlog_events,
std::move(events.to_notification_manager));
send_closure_later(notification_settings_manager_actor_, &NotificationSettingsManager::on_binlog_events,
std::move(events.to_notification_settings_manager));
send_closure(secret_chats_manager_, &SecretChatsManager::binlog_replay_finish);
}
void Td::init_options_and_network() {
VLOG(td_init) << "Create StateManager";
state_manager_ = create_actor<StateManager>("State manager", create_reference());
G()->set_state_manager(state_manager_.get());
VLOG(td_init) << "Create OptionManager";
option_manager_ = make_unique<OptionManager>(this);
G()->set_option_manager(option_manager_.get());
VLOG(td_init) << "Create ConnectionCreator";
G()->set_connection_creator(create_actor<ConnectionCreator>("ConnectionCreator", create_reference()));
complete_pending_preauthentication_requests([](int32 id) {
switch (id) {
case td_api::setNetworkType::ID:
case td_api::addProxy::ID:
case td_api::editProxy::ID:
case td_api::enableProxy::ID:
case td_api::disableProxy::ID:
case td_api::removeProxy::ID:
case td_api::getProxies::ID:
case td_api::getProxyLink::ID:
return true;
default:
return false;
}
});
VLOG(td_init) << "Create TempAuthKeyWatchdog";
G()->set_temp_auth_key_watchdog(create_actor<TempAuthKeyWatchdog>("TempAuthKeyWatchdog", create_reference()));
VLOG(td_init) << "Create ConfigManager";
config_manager_ = create_actor<ConfigManager>("ConfigManager", create_reference());
G()->set_config_manager(config_manager_.get());
VLOG(td_init) << "Create OnlineManager";
online_manager_ = make_unique<OnlineManager>(this, create_reference());
online_manager_actor_ = register_actor("OnlineManager", online_manager_.get());
G()->set_online_manager(online_manager_actor_.get());
}
void Td::init_file_manager() {
VLOG(td_init) << "Create FileManager";
class FileManagerContext final : public FileManager::Context {
public:
explicit FileManagerContext(Td *td) : td_(td) {
}
bool need_notify_on_new_files() final {
return !td_->auth_manager_->is_bot();
}
void on_new_file(int64 size, int64 real_size, int32 cnt) final {
send_closure(G()->storage_manager(), &StorageManager::on_new_file, size, real_size, cnt);
}
void on_file_updated(FileId file_id) final {
send_closure(G()->td(), &Td::send_update,
make_tl_object<td_api::updateFile>(td_->file_manager_->get_file_object(file_id)));
}
bool add_file_source(FileId file_id, FileSourceId file_source_id) final {
return td_->file_reference_manager_->add_file_source(file_id, file_source_id);
}
bool remove_file_source(FileId file_id, FileSourceId file_source_id) final {
return td_->file_reference_manager_->remove_file_source(file_id, file_source_id);
}
void on_merge_files(FileId to_file_id, FileId from_file_id) final {
td_->file_reference_manager_->merge(to_file_id, from_file_id);
}
vector<FileSourceId> get_some_file_sources(FileId file_id) final {
return td_->file_reference_manager_->get_some_file_sources(file_id);
}
void repair_file_reference(FileId file_id, Promise<Unit> promise) final {
send_closure(G()->file_reference_manager(), &FileReferenceManager::repair_file_reference, file_id,
std::move(promise));
}
void reload_photo(PhotoSizeSource source, Promise<Unit> promise) final {
FileReferenceManager::reload_photo(std::move(source), std::move(promise));
}
bool keep_exact_remote_location() final {
return !td_->auth_manager_->is_bot();
}
ActorShared<> create_reference() final {
return td_->create_reference();
}
private:
Td *td_;
};
file_manager_ = make_unique<FileManager>(make_unique<FileManagerContext>(this));
file_manager_actor_ = register_actor("FileManager", file_manager_.get());
file_manager_->init_actor();
G()->set_file_manager(file_manager_actor_.get());
file_reference_manager_ = make_unique<FileReferenceManager>(create_reference());
file_reference_manager_actor_ = register_actor("FileReferenceManager", file_reference_manager_.get());
G()->set_file_reference_manager(file_reference_manager_actor_.get());
}
void Td::init_non_actor_managers() {
VLOG(td_init) << "Create Managers";
audios_manager_ = make_unique<AudiosManager>(this);
callback_queries_manager_ = make_unique<CallbackQueriesManager>(this);
documents_manager_ = make_unique<DocumentsManager>(this);
videos_manager_ = make_unique<VideosManager>(this);
}
void Td::init_managers() {
account_manager_ = make_unique<AccountManager>(this, create_reference());
account_manager_actor_ = register_actor("AccountManager", account_manager_.get());
G()->set_account_manager(account_manager_actor_.get());
animations_manager_ = make_unique<AnimationsManager>(this, create_reference());
animations_manager_actor_ = register_actor("AnimationsManager", animations_manager_.get());
G()->set_animations_manager(animations_manager_actor_.get());
attach_menu_manager_ = make_unique<AttachMenuManager>(this, create_reference());
attach_menu_manager_actor_ = register_actor("AttachMenuManager", attach_menu_manager_.get());
G()->set_attach_menu_manager(attach_menu_manager_actor_.get());
autosave_manager_ = make_unique<AutosaveManager>(this, create_reference());
autosave_manager_actor_ = register_actor("AutosaveManager", autosave_manager_.get());
G()->set_autosave_manager(autosave_manager_actor_.get());
background_manager_ = make_unique<BackgroundManager>(this, create_reference());
background_manager_actor_ = register_actor("BackgroundManager", background_manager_.get());
G()->set_background_manager(background_manager_actor_.get());
boost_manager_ = make_unique<BoostManager>(this, create_reference());
boost_manager_actor_ = register_actor("BoostManager", boost_manager_.get());
G()->set_boost_manager(boost_manager_actor_.get());
bot_info_manager_ = make_unique<BotInfoManager>(this, create_reference());
bot_info_manager_actor_ = register_actor("BotInfoManager", bot_info_manager_.get());
G()->set_bot_info_manager(bot_info_manager_actor_.get());
business_connection_manager_ = make_unique<BusinessConnectionManager>(this, create_reference());
business_connection_manager_actor_ = register_actor("BusinessConnectionManager", business_connection_manager_.get());
G()->set_business_connection_manager(business_connection_manager_actor_.get());
business_manager_ = make_unique<BusinessManager>(this, create_reference());
business_manager_actor_ = register_actor("BusinessManager", business_manager_.get());
G()->set_business_manager(business_manager_actor_.get());
channel_recommendation_manager_ = make_unique<ChannelRecommendationManager>(this, create_reference());
channel_recommendation_manager_actor_ =
register_actor("ChannelRecommendationManager", channel_recommendation_manager_.get());
chat_manager_ = make_unique<ChatManager>(this, create_reference());
chat_manager_actor_ = register_actor("ChatManager", chat_manager_.get());
G()->set_chat_manager(chat_manager_actor_.get());
common_dialog_manager_ = make_unique<CommonDialogManager>(this, create_reference());
common_dialog_manager_actor_ = register_actor("CommonDialogManager", common_dialog_manager_.get());
connection_state_manager_ = make_unique<ConnectionStateManager>(this, create_reference());
connection_state_manager_actor_ = register_actor("ConnectionStateManager", connection_state_manager_.get());
country_info_manager_ = make_unique<CountryInfoManager>(this, create_reference());
country_info_manager_actor_ = register_actor("CountryInfoManager", country_info_manager_.get());
dialog_action_manager_ = make_unique<DialogActionManager>(this, create_reference());
dialog_action_manager_actor_ = register_actor("DialogActionManager", dialog_action_manager_.get());
G()->set_dialog_action_manager(dialog_action_manager_actor_.get());
dialog_filter_manager_ = make_unique<DialogFilterManager>(this, create_reference());
dialog_filter_manager_actor_ = register_actor("DialogFilterManager", dialog_filter_manager_.get());
G()->set_dialog_filter_manager(dialog_filter_manager_actor_.get());
dialog_invite_link_manager_ = make_unique<DialogInviteLinkManager>(this, create_reference());
dialog_invite_link_manager_actor_ = register_actor("DialogInviteLinkManager", dialog_invite_link_manager_.get());
G()->set_dialog_invite_link_manager(dialog_invite_link_manager_actor_.get());
dialog_manager_ = make_unique<DialogManager>(this, create_reference());
dialog_manager_actor_ = register_actor("DialogManager", dialog_manager_.get());
G()->set_dialog_manager(dialog_manager_actor_.get());
dialog_participant_manager_ = make_unique<DialogParticipantManager>(this, create_reference());
dialog_participant_manager_actor_ = register_actor("DialogParticipantManager", dialog_participant_manager_.get());
G()->set_dialog_participant_manager(dialog_participant_manager_actor_.get());
download_manager_ = DownloadManager::create(td::make_unique<DownloadManagerCallback>(this, create_reference()));
download_manager_actor_ = register_actor("DownloadManager", download_manager_.get());
G()->set_download_manager(download_manager_actor_.get());
forum_topic_manager_ = make_unique<ForumTopicManager>(this, create_reference());
forum_topic_manager_actor_ = register_actor("ForumTopicManager", forum_topic_manager_.get());
G()->set_forum_topic_manager(forum_topic_manager_actor_.get());
game_manager_ = make_unique<GameManager>(this, create_reference());
game_manager_actor_ = register_actor("GameManager", game_manager_.get());
G()->set_game_manager(game_manager_actor_.get());
group_call_manager_ = make_unique<GroupCallManager>(this, create_reference());
group_call_manager_actor_ = register_actor("GroupCallManager", group_call_manager_.get());
G()->set_group_call_manager(group_call_manager_actor_.get());
inline_message_manager_ = make_unique<InlineMessageManager>(this, create_reference());
inline_message_manager_actor_ = register_actor("InlineMessageManager", inline_message_manager_.get());
G()->set_inline_message_manager(inline_message_manager_actor_.get());
inline_queries_manager_ = make_unique<InlineQueriesManager>(this, create_reference());
inline_queries_manager_actor_ = register_actor("InlineQueriesManager", inline_queries_manager_.get());
link_manager_ = make_unique<LinkManager>(this, create_reference());
link_manager_actor_ = register_actor("LinkManager", link_manager_.get());
G()->set_link_manager(link_manager_actor_.get());
message_import_manager_ = make_unique<MessageImportManager>(this, create_reference());
message_import_manager_actor_ = register_actor("MessageImportManager", message_import_manager_.get());
G()->set_message_import_manager(message_import_manager_actor_.get());
messages_manager_ = make_unique<MessagesManager>(this, create_reference());
messages_manager_actor_ = register_actor("MessagesManager", messages_manager_.get());
G()->set_messages_manager(messages_manager_actor_.get());
notification_manager_ = make_unique<NotificationManager>(this, create_reference());
notification_manager_actor_ = register_actor("NotificationManager", notification_manager_.get());
G()->set_notification_manager(notification_manager_actor_.get());
notification_settings_manager_ = make_unique<NotificationSettingsManager>(this, create_reference());
notification_settings_manager_actor_ =
register_actor("NotificationSettingsManager", notification_settings_manager_.get());
G()->set_notification_settings_manager(notification_settings_manager_actor_.get());
people_nearby_manager_ = make_unique<PeopleNearbyManager>(this, create_reference());
people_nearby_manager_actor_ = register_actor("PeopleNearbyManager", people_nearby_manager_.get());
G()->set_people_nearby_manager(people_nearby_manager_actor_.get());
phone_number_manager_ = make_unique<PhoneNumberManager>(this, create_reference());
phone_number_manager_actor_ = register_actor("PhoneNumberManager", phone_number_manager_.get());
poll_manager_ = make_unique<PollManager>(this, create_reference());
poll_manager_actor_ = register_actor("PollManager", poll_manager_.get());
privacy_manager_ = make_unique<PrivacyManager>(this, create_reference());
privacy_manager_actor_ = register_actor("PrivacyManager", privacy_manager_.get());
promo_data_manager_ = make_unique<PromoDataManager>(this, create_reference());
promo_data_manager_actor_ = register_actor("PromoDataManager", promo_data_manager_.get());
G()->set_promo_data_manager(promo_data_manager_actor_.get());
quick_reply_manager_ = make_unique<QuickReplyManager>(this, create_reference());
quick_reply_manager_actor_ = register_actor("QuickReplyManager", quick_reply_manager_.get());
G()->set_quick_reply_manager(quick_reply_manager_actor_.get());
reaction_manager_ = make_unique<ReactionManager>(this, create_reference());
reaction_manager_actor_ = register_actor("ReactionManager", reaction_manager_.get());
G()->set_reaction_manager(reaction_manager_actor_.get());
saved_messages_manager_ = make_unique<SavedMessagesManager>(this, create_reference());
saved_messages_manager_actor_ = register_actor("SavedMessagesManager", saved_messages_manager_.get());
G()->set_saved_messages_manager(saved_messages_manager_actor_.get());
sponsored_message_manager_ = make_unique<SponsoredMessageManager>(this, create_reference());
sponsored_message_manager_actor_ = register_actor("SponsoredMessageManager", sponsored_message_manager_.get());
G()->set_sponsored_message_manager(sponsored_message_manager_actor_.get());
star_manager_ = make_unique<StarManager>(this, create_reference());
star_manager_actor_ = register_actor("StarManager", star_manager_.get());
G()->set_star_manager(star_manager_actor_.get());
statistics_manager_ = make_unique<StatisticsManager>(this, create_reference());
statistics_manager_actor_ = register_actor("StatisticsManager", statistics_manager_.get());
stickers_manager_ = make_unique<StickersManager>(this, create_reference());
stickers_manager_actor_ = register_actor("StickersManager", stickers_manager_.get());
G()->set_stickers_manager(stickers_manager_actor_.get());
memory_manager_ = make_unique<MemoryManager>(this, create_reference());
memory_manager_actor_ = register_actor("MemoryManager", memory_manager_.get());
G()->set_memory_manager(memory_manager_actor_.get());
story_manager_ = make_unique<StoryManager>(this, create_reference());
story_manager_actor_ = register_actor("StoryManager", story_manager_.get());
G()->set_story_manager(story_manager_actor_.get());
terms_of_service_manager_ = make_unique<TermsOfServiceManager>(this, create_reference());
terms_of_service_manager_actor_ = register_actor("TermsOfServiceManager", terms_of_service_manager_.get());
theme_manager_ = make_unique<ThemeManager>(this, create_reference());
theme_manager_actor_ = register_actor("ThemeManager", theme_manager_.get());
G()->set_theme_manager(theme_manager_actor_.get());
time_zone_manager_ = make_unique<TimeZoneManager>(this, create_reference());
time_zone_manager_actor_ = register_actor("TimeZoneManager", time_zone_manager_.get());
G()->set_time_zone_manager(time_zone_manager_actor_.get());
top_dialog_manager_ = make_unique<TopDialogManager>(this, create_reference());
top_dialog_manager_actor_ = register_actor("TopDialogManager", top_dialog_manager_.get());
G()->set_top_dialog_manager(top_dialog_manager_actor_.get());
transcription_manager_ = make_unique<TranscriptionManager>(this, create_reference());
transcription_manager_actor_ = register_actor("TranscriptionManager", transcription_manager_.get());
G()->set_transcription_manager(transcription_manager_actor_.get());
translation_manager_ = make_unique<TranslationManager>(this, create_reference());
translation_manager_actor_ = register_actor("TranslationManager", translation_manager_.get());
updates_manager_ = make_unique<UpdatesManager>(this, create_reference());
updates_manager_actor_ = register_actor("UpdatesManager", updates_manager_.get());
G()->set_updates_manager(updates_manager_actor_.get());
user_manager_ = make_unique<UserManager>(this, create_reference());
user_manager_actor_ = register_actor("UserManager", user_manager_.get());
G()->set_user_manager(user_manager_actor_.get());
video_notes_manager_ = make_unique<VideoNotesManager>(this, create_reference());
video_notes_manager_actor_ = register_actor("VideoNotesManager", video_notes_manager_.get());
voice_notes_manager_ = make_unique<VoiceNotesManager>(this, create_reference());
voice_notes_manager_actor_ = register_actor("VoiceNotesManager", voice_notes_manager_.get());
web_pages_manager_ = make_unique<WebPagesManager>(this, create_reference());
web_pages_manager_actor_ = register_actor("WebPagesManager", web_pages_manager_.get());
G()->set_web_pages_manager(web_pages_manager_actor_.get());
}
void Td::init_pure_actor_managers() {
call_manager_ = create_actor<CallManager>("CallManager", create_reference());
G()->set_call_manager(call_manager_.get());
cashtag_search_hints_ = create_actor<HashtagHints>("CashtagSearchHints", "cashtag_search", '$', create_reference());
device_token_manager_ = create_actor<DeviceTokenManager>("DeviceTokenManager", create_reference());
hashtag_hints_ = create_actor<HashtagHints>("HashtagHints", "text", '#', create_reference());
hashtag_search_hints_ = create_actor<HashtagHints>("HashtagSearchHints", "search", '#', create_reference());
language_pack_manager_ = create_actor<LanguagePackManager>("LanguagePackManager", create_reference());
G()->set_language_pack_manager(language_pack_manager_.get());
password_manager_ = create_actor<PasswordManager>("PasswordManager", create_reference());
G()->set_password_manager(password_manager_.get());
secure_manager_ = create_actor<SecureManager>("SecureManager", create_reference());
}
void Td::send_update(tl_object_ptr<td_api::Update> &&object) {
CHECK(object != nullptr);
auto object_id = object->get_id();
if (close_flag_ >= 5 && object_id != td_api::updateAuthorizationState::ID) {
// just in case
return;
}
switch (object_id) {
case td_api::updateAccentColors::ID:
case td_api::updateChatThemes::ID:
case td_api::updateFavoriteStickers::ID:
case td_api::updateInstalledStickerSets::ID:
case td_api::updateProfileAccentColors::ID:
case td_api::updateRecentStickers::ID:
case td_api::updateSavedAnimations::ID:
case td_api::updateSavedNotificationSounds::ID:
case td_api::updateUserStatus::ID:
VLOG(td_requests) << "Sending update: " << oneline(to_string(object));
break;
case td_api::updateTrendingStickerSets::ID: {
auto update = static_cast<const td_api::updateTrendingStickerSets *>(object.get());
auto sticker_sets = update->sticker_sets_.get();
VLOG(td_requests) << "Sending update: updateTrendingStickerSets { " << oneline(to_string(update->sticker_type_))
<< ", total_count = " << sticker_sets->total_count_
<< ", count = " << sticker_sets->sets_.size() << " }";
break;
}
case td_api::updateOption::ID:
if (auth_manager_ == nullptr || !auth_manager_->is_bot()) {
VLOG(td_requests) << "Sending update: " << to_string(object);
}
break;
case td_api::updateDefaultReactionType::ID / 2:
LOG(ERROR) << "Sending update: " << oneline(to_string(object));
break;
default:
VLOG(td_requests) << "Sending update: " << to_string(object);
}
callback_->on_result(0, std::move(object));
}
void Td::send_result(uint64 id, tl_object_ptr<td_api::Object> object) {
if (id == 0) {
LOG(ERROR) << "Sending " << to_string(object) << " through send_result";
return;
}
auto it = request_set_.find(id);
if (it != request_set_.end()) {
if (object == nullptr) {
object = make_tl_object<td_api::error>(404, "Not Found");
}
VLOG(td_requests) << "Sending result for request " << id << ": " << to_string(object);
request_set_.erase(it);
callback_->on_result(id, std::move(object));
}
}
void Td::send_error_impl(uint64 id, tl_object_ptr<td_api::error> error) {
CHECK(id != 0);
CHECK(error != nullptr);
auto it = request_set_.find(id);
if (it != request_set_.end()) {
if (error->code_ == 0 && error->message_ == "Lost promise") {
LOG(FATAL) << "Lost promise for query " << id << " of type " << it->second << " in close state " << close_flag_;
}
VLOG(td_requests) << "Sending error for request " << id << ": " << oneline(to_string(error));
request_set_.erase(it);
callback_->on_error(id, std::move(error));
}
}
void Td::send_error(uint64 id, Status error) {
send_error_impl(id, make_tl_object<td_api::error>(error.code(), error.message().str()));
error.ignore();
}
Result<std::pair<Td::Parameters, TdDb::Parameters>> Td::get_parameters(
td_api::object_ptr<td_api::setTdlibParameters> parameters) {
VLOG(td_init) << "Begin to set TDLib parameters";
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_)) {
VLOG(td_init) << "Wrong string encoding";
return Status::Error(400, "Strings must be encoded in UTF-8");
}
if (parameters->api_id_ <= 0) {
return Status::Error(400, "Valid api_id must be provided. Can be obtained at https://my.telegram.org");
}
if (parameters->api_hash_.empty()) {
return Status::Error(400, "Valid api_hash must be provided. Can be obtained at https://my.telegram.org");
}
std::pair<Parameters, TdDb::Parameters> result;
result.first.api_id_ = parameters->api_id_;
result.first.api_hash_ = std::move(parameters->api_hash_);
result.first.use_secret_chats_ = parameters->use_secret_chats_;
result.second.encryption_key_ = TdDb::as_db_key(std::move(parameters->database_encryption_key_), Global::get_use_custom_database(parameters->database_directory_));
result.second.database_directory_ = Global::get_database_directory_path(parameters->database_directory_);
result.second.use_custom_database_format_ = Global::get_use_custom_database(parameters->database_directory_);
result.second.files_directory_ = std::move(parameters->files_directory_);
result.second.is_test_dc_ = parameters->use_test_dc_;
result.second.use_file_database_ = parameters->use_file_database_;
result.second.use_chat_info_database_ = parameters->use_chat_info_database_;
result.second.use_message_database_ = parameters->use_message_database_;
VLOG(td_init) << "Create MtprotoHeader::Options";
options_.api_id = parameters->api_id_;
options_.system_language_code = trim(parameters->system_language_code_);
options_.device_model = trim(parameters->device_model_);
options_.system_version = trim(parameters->system_version_);
options_.application_version = trim(parameters->application_version_);
if (options_.system_language_code.empty()) {
return Status::Error(400, "System language code must be non-empty");
}
if (options_.device_model.empty()) {
return Status::Error(400, "Device model must be non-empty");
}
if (options_.system_version.empty()) {
options_.system_version = get_operating_system_version().str();
VLOG(td_init) << "Set system version to " << options_.system_version;
}
if (options_.application_version.empty()) {
return Status::Error(400, "Application version must be non-empty");
}
if (options_.api_id != 21724) {
options_.application_version += ", TDLib ";
auto version = OptionManager::get_option_synchronously("version");
CHECK(version->get_id() == td_api::optionValueString::ID);
options_.application_version += static_cast<const td_api::optionValueString *>(version.get())->value_;
}
options_.language_pack = string();
options_.language_code = string();
options_.parameters = string();
options_.is_emulator = false;
options_.proxy = Proxy();
return std::move(result);
}
void Td::on_file_download_finished(FileId file_id) {
requests_->on_file_download_finished(file_id);
}
} // namespace td