#include #include #include #include #include #include #include "common.h" #include "commands.h" #include "td/telegram/td_api.h" #include "td/telegram/td_api.hpp" const char *APP_GREETING = "--------------------\n"\ "| shortener-bot v0 |\n"\ "--------------------\n\n"; void async_startup(uv_idle_t *h); void configure_blocked_signals(); void configure_logging(); void configure_shutdown_signals(uv_loop_t *loop, context *ctx); CommandLineParams parse_command_line(int argc, char **argv); void print_greeting(); void print_usage(const char *programName); void shutdown_signal_handler(uv_signal_t *h, int signum); int main(int argc, char **argv) { CommandLineParams cmdLineParams; try { cmdLineParams = parse_command_line(argc, argv); } catch (CommandLineParamError e) { switch (e) { case CommandLineParamError::NoApiCreds: print_usage(argv[0]); return 1; } } configure_logging(); configure_blocked_signals(); print_greeting(); uv_loop_t *defaultLoop = uv_default_loop(); context ctx; ctx.apiCreds = cmdLineParams.apiCreds; spdlog::info("Creating Telegram client"); TelegramClient tg(defaultLoop); ctx.tg = &tg; uv_idle_t startupHandle; startupHandle.data = (void*)&ctx; uv_idle_init(defaultLoop, &startupHandle); uv_idle_start(&startupHandle, async_startup); configure_shutdown_signals(defaultLoop, &ctx); spdlog::info("Entering event loop"); uv_run(defaultLoop, UV_RUN_DEFAULT); spdlog::info("Leaving event loop"); spdlog::info("Cleaning up"); return 0; } void async_startup(uv_idle_t *h) { context *ctx = (context*)h->data; spdlog::info("Starting telegram client (using api id {}, hash {})", ctx->apiCreds.id, ctx->apiCreds.hash); ctx->tg->add_update_handler([ctx](void*, td_api::Object &update){ td_api::downcast_call(update, overloaded( [ctx](td_api::updateAuthorizationState &upd) { td_api::downcast_call(*upd.authorization_state_, overloaded( [ctx](td_api::authorizationStateWaitTdlibParameters &state) { auto request = td_api::make_object(); request->database_directory_ = "tdata"; request->use_message_database_ = false; request->use_secret_chats_ = false; request->api_id_ = ctx->apiCreds.id; request->api_hash_ = ctx->apiCreds.hash; request->system_language_code_ = "en"; request->device_model_ = "server"; request->application_version_ = "1.0.0"; ctx->tg->send_query(std::move(request), {}); }, [ctx](td_api::authorizationStateWaitPhoneNumber &state) { ctx->tg->send_query(td_api::make_object(ctx->apiCreds.botToken), {}); // TODO add error handler }, [ctx](td_api::authorizationStateReady &state) { spdlog::info("Telegram ready"); }, [ctx,&upd](td_api::Object &obj){ spdlog::debug("unknown authorization state ID {}", upd.authorization_state_->get_id()); } )); }, [ctx](td_api::updateNewMessage &upd) { if (upd.message_->is_outgoing_) return; cmd::handle_regular_message(ctx, *upd.message_); }, [](td_api::Object &obj){} )); }, nullptr); ctx->tg->start(); uv_close((uv_handle_t*)h, nullptr); } void configure_logging() { auto loggingSink = std::make_shared>(stdout, spdlog::color_mode::automatic); loggingSink->set_color(spdlog::level::debug, "\033[38;5;61m"); auto logger = std::make_shared("main", loggingSink); spdlog::set_default_logger(logger); spdlog::set_level(spdlog::level::debug); } void configure_blocked_signals() { sigset_t signalSet; sigemptyset(&signalSet); sigaddset(&signalSet, SIGUSR1); sigaddset(&signalSet, SIGUSR2); sigaddset(&signalSet, SIGHUP); sigprocmask(SIG_BLOCK, &signalSet, nullptr); } void register_signal_handle(uv_loop_t *loop, context *ctx, int signum, void(*signal_handler)(uv_signal_t*, int)) { auto deleter = [](uv_signal_t *handle) { uv_close((uv_handle_t*)handle, [](uv_handle_t *h){ delete h; }); }; std::unique_ptr handle(new uv_signal_t, deleter); uv_signal_init(loop, handle.get()); uv_signal_start(handle.get(), signal_handler, signum); } void configure_shutdown_signals(uv_loop_t *loop, context *ctx) { register_signal_handle(loop, ctx, SIGINT, shutdown_signal_handler); } void shutdown_signal_handler(uv_signal_t *h, int signum) { spdlog::info("Stopping"); uv_stop(uv_default_loop()); } CommandLineParams parse_command_line(int argc, char **argv) { if (argc < 4) throw CommandLineParamError::NoApiCreds; char *apiIdEnd = nullptr; int apiId = strtol(argv[1], &apiIdEnd, 10); if (apiIdEnd == argv[1]) throw CommandLineParamError::NoApiCreds; return CommandLineParams {apiId, std::string(argv[2]), std::string(argv[3])}; } void print_greeting() { std::cout << APP_GREETING << std::flush; } void print_usage(const char *programName) { std::cout << "usage: " << programName << " \n"; }