diff --git a/CMakeLists.txt b/CMakeLists.txt index 9579cb5..57c69ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,10 @@ -cmake_minimum_required(VERSION 3.0.2) +cmake_minimum_required(VERSION 3.5) project(shortener_bot LANGUAGES C CXX) -add_executable(bot main.cpp telegram_client.cpp) +add_executable(bot main.cpp telegram_client.cpp commands.cpp) +set_property(TARGET bot PROPERTY CXX_STANDARD 20) +set_property(TARGET bot PROPERTY CXX_STANDARD_REQUIRED ON) + add_library(libuv STATIC IMPORTED) set_target_properties(libuv PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libuv-v1.49.0/.libs/libuv.a) diff --git a/commands.cpp b/commands.cpp new file mode 100644 index 0000000..e09cb93 --- /dev/null +++ b/commands.cpp @@ -0,0 +1,24 @@ +#include "commands.h" +#include "td/telegram/td_api.h" +#include +#include +#include +#include + +void cmd::handle_regular_message(context *ctx, td_api::message &msg) { + if (msg.content_->get_id() == td_api::messageText::ID) { + std::string text = static_cast(*msg.content_).text_->text_; + if (std::strncmp(text.c_str(), "/shorten ", 9) == 0) { + std::string_view param(text.begin() + 9, text.end()); + size_t nextSpace = param.find(' '); + if (nextSpace != std::string_view::npos) + param.remove_suffix(param.size() - nextSpace); + spdlog::info("Command /shorten received with parameter '{}'", param); + } else if (std::strncmp(text.c_str(), "/shorten", 8) == 0) { + std::string textRaw("usage: /shorten "); + std::vector> empty; + auto text = static_cast>(td_api::make_object(td_api::make_object(textRaw, std::move(empty)), nullptr, false)); + ctx->tg->send_query(td_api::make_object(msg.chat_id_, msg.message_thread_id_, nullptr, nullptr, nullptr, std::move(text)), {}); + } + } +} \ No newline at end of file diff --git a/commands.h b/commands.h new file mode 100644 index 0000000..62ed557 --- /dev/null +++ b/commands.h @@ -0,0 +1,8 @@ +#pragma once +#include "common.h" +#include "telegram_client.h" +#include + +namespace cmd { + void handle_regular_message(context *ctx, td_api::message &msg); +} \ No newline at end of file diff --git a/common.h b/common.h index b44d433..d7bce24 100644 --- a/common.h +++ b/common.h @@ -1,4 +1,5 @@ #pragma once +#include namespace detail { template @@ -25,3 +26,25 @@ auto overloaded(F... f) { return detail::overload(f...); } + +enum class CommandLineParamError { + NoApiCreds +}; + +struct ApiCreds { + int id; + std::string hash; + std::string botToken; +}; + +struct CommandLineParams { + ApiCreds apiCreds; + std::string botToken; +}; + +struct TelegramClient; + +struct context { + TelegramClient *tg; + ApiCreds apiCreds; +}; \ No newline at end of file diff --git a/main.cpp b/main.cpp index cc12778..b20db4c 100644 --- a/main.cpp +++ b/main.cpp @@ -1,38 +1,53 @@ +#include #include #include +#include #include #include -#include "telegram_client.h" +#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"; -struct context { - TelegramClient *tg; -}; - 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 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); @@ -47,7 +62,45 @@ int main() { } void async_startup(uv_idle_t *h) { - spdlog::info("Starting up"); + 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); } @@ -86,6 +139,18 @@ void shutdown_signal_handler(uv_signal_t *h, int signum) { 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"; +} \ No newline at end of file