// // 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 #include #include #include #include #include #include #include #include #include #include // Simple single-threaded example of TDLib usage. // Real world programs should use separate thread for the user input. // Example includes user authentication, receiving updates, getting chat list and sending text messages. // overloaded namespace detail { template struct overload; template struct overload : public F { explicit overload(F f) : F(f) { } }; template struct overload : public overload , public overload { overload(F f, Fs... fs) : overload(f), overload(fs...) { } using overload::operator(); using overload::operator(); }; } // namespace detail template auto overloaded(F... f) { return detail::overload(f...); } namespace td_api = td::td_api; class TdExample { public: TdExample() { td::ClientManager::execute(td_api::make_object(1)); client_manager_ = std::make_unique(); client_id_ = client_manager_->create_client_id(); send_query(td_api::make_object("version"), {}); } void loop() { while (true) { if (need_restart_) { restart(); } else if (!are_authorized_) { process_response(client_manager_->receive(10)); } else { std::cout << "Enter action [q] quit [u] check for updates and request results [c] show chats [m " "] send message [me] show self [l] logout: " << std::endl; std::string line; std::getline(std::cin, line); std::istringstream ss(line); std::string action; if (!(ss >> action)) { continue; } if (action == "q") { return; } if (action == "u") { std::cout << "Checking for updates..." << std::endl; while (true) { auto response = client_manager_->receive(0); if (response.object) { process_response(std::move(response)); } else { break; } } } else if (action == "close") { std::cout << "Closing..." << std::endl; send_query(td_api::make_object(), {}); } else if (action == "me") { send_query(td_api::make_object(), [this](Object object) { std::cout << to_string(object) << std::endl; }); } else if (action == "l") { std::cout << "Logging out..." << std::endl; send_query(td_api::make_object(), {}); } else if (action == "m") { std::int64_t chat_id; ss >> chat_id; ss.get(); std::string text; std::getline(ss, text); std::cout << "Sending message to chat " << chat_id << "..." << std::endl; auto send_message = td_api::make_object(); send_message->chat_id_ = chat_id; auto message_content = td_api::make_object(); message_content->text_ = td_api::make_object(); message_content->text_->text_ = std::move(text); send_message->input_message_content_ = std::move(message_content); send_query(std::move(send_message), {}); } else if (action == "c") { std::cout << "Loading chat list..." << std::endl; send_query(td_api::make_object(nullptr, 20), [this](Object object) { if (object->get_id() == td_api::error::ID) { return; } auto chats = td::move_tl_object_as(object); for (auto chat_id : chats->chat_ids_) { std::cout << "[chat_id:" << chat_id << "] [title:" << chat_title_[chat_id] << "]" << std::endl; } }); } } } } private: using Object = td_api::object_ptr; std::unique_ptr client_manager_; std::int32_t client_id_{0}; td_api::object_ptr authorization_state_; bool are_authorized_{false}; bool need_restart_{false}; std::uint64_t current_query_id_{0}; std::uint64_t authentication_query_id_{0}; std::map> handlers_; std::map> users_; std::map chat_title_; void restart() { client_manager_.reset(); *this = TdExample(); } void send_query(td_api::object_ptr f, std::function handler) { auto query_id = next_query_id(); if (handler) { handlers_.emplace(query_id, std::move(handler)); } client_manager_->send(client_id_, query_id, std::move(f)); } void process_response(td::ClientManager::Response response) { if (!response.object) { return; } //std::cout << response.request_id << " " << to_string(response.object) << std::endl; if (response.request_id == 0) { return process_update(std::move(response.object)); } auto it = handlers_.find(response.request_id); if (it != handlers_.end()) { it->second(std::move(response.object)); handlers_.erase(it); } } std::string get_user_name(std::int64_t user_id) const { auto it = users_.find(user_id); if (it == users_.end()) { return "unknown user"; } return it->second->first_name_ + " " + it->second->last_name_; } std::string get_chat_title(std::int64_t chat_id) const { auto it = chat_title_.find(chat_id); if (it == chat_title_.end()) { return "unknown chat"; } return it->second; } void process_update(td_api::object_ptr update) { td_api::downcast_call( *update, overloaded( [this](td_api::updateAuthorizationState &update_authorization_state) { authorization_state_ = std::move(update_authorization_state.authorization_state_); on_authorization_state_update(); }, [this](td_api::updateNewChat &update_new_chat) { chat_title_[update_new_chat.chat_->id_] = update_new_chat.chat_->title_; }, [this](td_api::updateChatTitle &update_chat_title) { chat_title_[update_chat_title.chat_id_] = update_chat_title.title_; }, [this](td_api::updateUser &update_user) { auto user_id = update_user.user_->id_; users_[user_id] = std::move(update_user.user_); }, [this](td_api::updateNewMessage &update_new_message) { auto chat_id = update_new_message.message_->chat_id_; std::string sender_name; td_api::downcast_call(*update_new_message.message_->sender_id_, overloaded( [this, &sender_name](td_api::messageSenderUser &user) { sender_name = get_user_name(user.user_id_); }, [this, &sender_name](td_api::messageSenderChat &chat) { sender_name = get_chat_title(chat.chat_id_); })); std::string text; if (update_new_message.message_->content_->get_id() == td_api::messageText::ID) { text = static_cast(*update_new_message.message_->content_).text_->text_; } std::cout << "Got message: [chat_id:" << chat_id << "] [from:" << sender_name << "] [" << text << "]" << std::endl; }, [](auto &update) {})); } auto create_authentication_query_handler() { return [this, id = authentication_query_id_](Object object) { if (id == authentication_query_id_) { check_authentication_error(std::move(object)); } }; } 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(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(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(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(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(), create_authentication_query_handler()); } else { send_query(td_api::make_object(std::move(key)), create_authentication_query_handler()); } }, [this](td_api::authorizationStateWaitTdlibParameters &) { auto parameters = td_api::make_object(); 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(std::move(parameters)), create_authentication_query_handler()); })); } void check_authentication_error(Object object) { if (object->get_id() == td_api::error::ID) { auto error = td::move_tl_object_as(object); std::cout << "Error: " << to_string(error) << std::flush; on_authorization_state_update(); } } std::uint64_t next_query_id() { return ++current_query_id_; } }; int main() { TdExample example; example.loop(); }