commit 5a55dc297cf65252d2abb6c68432925abff8449a Author: Saturneric Date: Sun May 17 07:42:43 2020 -0700 Initial Commit. diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..773bbcf --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.16) +project(tgclic) + +set(CMAKE_CXX_STANDARD 14) + +find_package(Td 1.6.4 REQUIRED) + +add_executable(tgclic main.cpp application.h utils.h respond_processor.h tg.h info_pool.h request_processor.h) +target_link_libraries(tgclic PRIVATE Td::TdStatic) +set_property(TARGET tgclic PROPERTY CXX_STANDARD 14) \ No newline at end of file diff --git a/application.h b/application.h new file mode 100644 index 0000000..d8d99bf --- /dev/null +++ b/application.h @@ -0,0 +1,313 @@ +// +// Created by eric on 5/13/20. +// + +#ifndef TGCLIC_APPLICATION_H +#define TGCLIC_APPLICATION_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "utils.h" + +#include "request_processor.h" +#include "respond_processor.h" + +using std::unique_ptr; +using std::map; +using namespace td; + +namespace tgclc { + + class Application { + public: + Application() { + + } + + void loop() { + + send_query(td_api::make_object( + "192.168.20.1", + 7891, + true, + td_api::make_object()), + [this](Object object) { std::cout << to_string(object) << std::endl; }); + + while (true) { + + if (need_restart_) { + restart(); + } else if (!are_authorized_) { + process_response(infoPool.getClient().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 = infoPool.getClient().receive(0); + if (response.object) { + respondProcessor.deal(std::move(response)); + } else { + break; + } + } + } else if (action == "close") { + std::cout << "Closing..." << std::endl; + requestProcessor.send(td_api::make_object(), {}); + } else if (action == "me") { + requestProcessor.send(td_api::make_object(), + [this](Object object) { std::cout << to_string(object) << std::endl; }); + } else if (action == "l") { + std::cout << "Logging out..." << std::endl; + requestProcessor.send(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); + + requestProcessor.send(std::move(send_message), {}); + } else if (action == "c") { + std::cout << "Loading chat list..." << std::endl; + requestProcessor.send( + td_api::make_object(nullptr, std::numeric_limits::max(), + 0, 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 << "[id:" << chat_id << "] [title:" << chat_title_[chat_id] << "]" + << std::endl; + } + }); + } + } + } + } + + private: + using Object = td_api::object_ptr; + + 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> users_; + + std::map chat_title_; + + std::map> handlers_; + + RespondProcessor& respondProcessor = singleton::instance(); + + RequestProcessor& requestProcessor = singleton::instance(); + + InfoPool& infoPool = singleton::instance(); + + void restart() { + + } + + std::string get_user_name(std::int32_t user_id) { + auto it = users_.find(user_id); + if (it == users_.end()) { + return "unknown user"; + } + return it->second->first_name_ + " " + it->second->last_name_; + } + + void send_query(td_api::object_ptr f, std::function handler) { + auto query_id = infoPool.createHandler(handler); + if (handler) { + // handlers_.emplace(query_id, std::move(handler)); + } + infoPool.getClient().send({query_id, std::move(f)}); + } + + void process_response(td::Client::Response response) { + if (!response.object) { + return; + } + + if (response.id == 0) { + return process_update(std::move(response.object)); + } + + infoPool.setHandlerRespondObject(response.id, std::move(response.object)); + } + + 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_; + auto sender_user_name = get_user_name(update_new_message.message_->sender_user_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_user_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; + requestProcessor.send(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; + requestProcessor.send(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::cin >> password; + requestProcessor.send(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; + requestProcessor.send(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") { + requestProcessor.send(td_api::make_object(), + create_authentication_query_handler()); + } else { + requestProcessor.send(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_ = 1368212; + parameters->api_hash_ = "8f5f44883e9d82d8112390fea55dbf47"; + parameters->system_language_code_ = "zh"; + parameters->device_model_ = "Desktop"; + parameters->system_version_ = "Unknown"; + parameters->application_version_ = "1.0"; + parameters->enable_storage_optimizer_ = true; + requestProcessor.send(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_; + } + }; + +} + +#endif //TGCLIC_APPLICATION_H diff --git a/info_pool.h b/info_pool.h new file mode 100644 index 0000000..236ccf5 --- /dev/null +++ b/info_pool.h @@ -0,0 +1,62 @@ +// +// Created by eric on 5/17/20. +// + +#ifndef TGCLIC_INFO_POOL_H +#define TGCLIC_INFO_POOL_H + +#include +#include +#include +#include + +#include "tg.h" +#include "utils.h" + +namespace tgclc { + + class InfoPool { + public: + + InfoPool(){ + td::Client::execute({0, td_api::make_object(1)}); + client_ = std::make_unique(); + } + + void setHandlerRespondObject(std::uint64_t id, object_p&& object) { + auto it = handlers_.find(id); + if (it != handlers_.end()) { + it->second(std::move(object)); + } + } + + auto createHandler(std::function handler){ + auto query_id = get_new_query_id(); + if (handler) { + handlers_.emplace(query_id, std::move(handler)); + } + return query_id; + } + + td::Client& getClient(){ + return *client_; + } + + + private: + + std::uint64_t current_query_id_{0}; + + std::map> handlers_; + + std::unique_ptr client_; + + std::uint64_t get_new_query_id(){ + return ++current_query_id_; + } + }; + +} + + +#endif //TGCLIC_INFO_POOL_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..02cd19d --- /dev/null +++ b/main.cpp @@ -0,0 +1,9 @@ +#include + +#include "application.h" + +int main() { + tgclc::Application app; + app.loop(); + return 0; +} diff --git a/request_processor.h b/request_processor.h new file mode 100644 index 0000000..3486993 --- /dev/null +++ b/request_processor.h @@ -0,0 +1,30 @@ +// +// Created by eric on 5/17/20. +// + +#ifndef TGCLIC_REQUEST_PROCESSOR_H +#define TGCLIC_REQUEST_PROCESSOR_H + +#include "tg.h" +#include "utils.h" +#include "info_pool.h" + +namespace tgclc { + + class RequestProcessor { + public: + + void send(td_api::object_ptr f, std::function handler) { + auto query_id = infoPool.createHandler(std::move(handler)); + infoPool.getClient().send({query_id, std::move(f)}); + } + + private: + InfoPool& infoPool = singleton::instance(); + + }; + +} + + +#endif //TGCLIC_REQUEST_PROCESSOR_H diff --git a/tg.h b/tg.h new file mode 100644 index 0000000..01694fd --- /dev/null +++ b/tg.h @@ -0,0 +1,21 @@ +// +// Created by eric on 5/17/20. +// + +#ifndef TGCLIC_TG_H +#define TGCLIC_TG_H + +#include +#include +#include + +using namespace td; + +using object = td_api::Object; +using object_p = td_api::object_ptr; +using object_r = td_api::Object &; + + +using Object = object_p; + +#endif //TGCLIC_TG_H diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..3115712 --- /dev/null +++ b/utils.h @@ -0,0 +1,53 @@ +// +// Created by eric on 5/13/20. +// + +#ifndef TGCLIC_UTILS_H +#define TGCLIC_UTILS_H + + +#include + +namespace detail { + template + struct overload; + + template + struct overload : public F { + explicit overload(F f) : F(f) { + } + }; + template + struct overload + : public overload + , 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...); +} + +template +class singleton +{ +private: + static std::unique_ptr p; + +public: + static T& instance(){ + return *p; + } +}; + +template +std::unique_ptr singleton::p = std::make_unique(); + +#endif //TGCLIC_UTILS_H