#include #include #include #include "tg.h" #include "td/telegram/td_api.h" using namespace tg; TelegramClient::TelegramClient(uv_loop_t *eventLoop, long apiId, std::string apiHash, std::string phoneNumber) : m_eventLoop(eventLoop), m_apiId(apiId), m_apiHash(apiHash), m_phoneNumber(phoneNumber) {} bool TelegramClient::start() { spdlog::debug("tg: start"); if (m_running) return false; m_running = true; spdlog::debug("tg: creating uv handle"); m_uvHandle = new uv_async_t; m_uvHandle->data = (void*)this; uv_async_init(m_eventLoop, m_uvHandle, TelegramClient::uv_callback); spdlog::debug("tg: starting telegram thread"); m_thread = std::thread([this](){ run_thread(); }); return true; } TelegramClient::~TelegramClient() { if (m_thread.joinable()) { send_query(td_api::make_object(), {}); m_thread.join(); if (m_uvHandle) uv_close((uv_handle_t*)m_uvHandle, TelegramClient::uv_close_handle_callback); } } void TelegramClient::run_thread() { spdlog::debug("tg: i am telegram thread"); td::ClientManager::execute(td_api::make_object(0)); m_clientManager = std::make_unique(); m_clientId = m_clientManager->create_client_id(); send_query(td_api::make_object("version"), {}); run_telegram_main_loop(); } void TelegramClient::run_telegram_main_loop() { spdlog::info("tg: begin telegram main loop"); while (m_running) { if (!process_response(m_clientManager->receive(500))) { spdlog::info("tg: closed"); return; } } } void TelegramClient::send_query(td_api::object_ptr f, std::function callback) { std::uint64_t query_id = m_nextQueryId++; if (callback) { m_handlers.emplace(query_id, std::move(callback)); } m_clientManager->send(m_clientId, query_id, std::move(f)); } bool TelegramClient::process_response(td::ClientManager::Response response) { if (!response.object) { return true; } if (response.object->get_id() == td_api::updateAuthorizationState::ID) { auto authUpdate = (td_api::updateAuthorizationState*) response.object.get(); spdlog::debug("updateAuthorizationState: {}", authUpdate->authorization_state_->get_id()); switch (authUpdate->authorization_state_->get_id()) { case td_api::authorizationStateClosed::ID: spdlog::debug("closed"); return false; case td_api::authorizationStateWaitTdlibParameters::ID: spdlog::debug("setting tdlib parameters"); set_tdlib_parameters(); return true; case td_api::authorizationStateWaitPhoneNumber::ID: spdlog::debug("setting phone number"); set_phone_number(); return true; case td_api::authorizationStateWaitPassword::ID: spdlog::debug("setting password"); if (passwordProvider) passwordProvider(std::bind(&TelegramClient::set_password, this, std::placeholders::_1)); return true; case td_api::authorizationStateWaitCode::ID: spdlog::debug("setting 2FA code"); if (authCodeProvider) authCodeProvider(std::bind(&TelegramClient::set_code, this, std::placeholders::_1)); return true; } } m_responseQueueMutex.lock(); m_responseQueue.push_back(std::move(response)); m_responseQueueMutex.unlock(); uv_async_send(m_uvHandle); return true; } void TelegramClient::set_tdlib_parameters() { auto query = td_api::make_object(); query->database_directory_ = "tdata"; query->use_message_database_ = true; query->use_secret_chats_ = false; query->api_id_ = m_apiId; query->api_hash_ = m_apiHash; query->system_language_code_ = "en"; query->device_model_ = "server"; query->application_version_ = "0.1.0"; send_query(std::move(query), {}); } void TelegramClient::set_phone_number() { auto query = td_api::make_object(); query->phone_number_ = m_phoneNumber; send_query(std::move(query), {}); } void TelegramClient::set_code(std::string code) { spdlog::debug("setting auth code {}", code); send_query(td_api::make_object(code), {}); } void TelegramClient::set_password(std::string password) { spdlog::debug("setting password {}", password); send_query(td_api::make_object(password), {}); } void TelegramClient::uv_callback(uv_async_t *h) { //spdlog::debug("tg: libuv callback"); TelegramClient *self = (TelegramClient*)h->data; self->m_responseQueueMutex.lock(); while (self->m_responseQueue.size() > 0) { auto response = std::move(self->m_responseQueue.front()); self->m_responseQueue.pop_front(); if (response.request_id == 0) { self->dispatch_update(response.object); } else { self->dispatch_response(response.request_id, std::move(response.object)); } } self->m_responseQueueMutex.unlock(); } void TelegramClient::dispatch_update(TgObject &update) { //spdlog::debug("tg: update"); for (auto i = m_updateHandlers.begin(); i != m_updateHandlers.end(); i++) { i->first(i->second, *update.get()); } } void TelegramClient::dispatch_response(std::uint64_t queryId, TgObject response) { spdlog::debug("tg: response to {}", queryId); auto queryHandler = m_handlers.find(queryId); if (queryHandler != m_handlers.end()) { queryHandler->second(std::move(response)); } } void TelegramClient::uv_close_handle_callback(uv_handle_t *h) { spdlog::debug("tg: closing uv handle"); delete h; } void TelegramClient::add_update_handler(std::function handler, void *context) { m_updateHandlers.push_back({std::move(handler), context}); }