├── .clang_complete ├── .gitmodules ├── CMakeLists.txt ├── cliclient.cpp ├── cliclient.hpp ├── clilua.cpp ├── clilua.hpp ├── json.hpp ├── main.cpp └── telegram.h /.clang_complete: -------------------------------------------------------------------------------- 1 | -iquote td 2 | -iquote td/tdtl 3 | -iquote td/tdutils 4 | -iquote td/tdactor 5 | -iquote td/td/generate 6 | -iquote td/td/generate/default_td_api 7 | -I /usr/include/lua5.1 8 | -std=c++14 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "td"] 2 | path = td 3 | url = https://github.com/tdlib/td 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | add_subdirectory("td/") 4 | include_directories ("td/") 5 | include_directories ("td/tdactor/") 6 | include_directories ("td/tdutils/") 7 | include_directories ("td/tdtl/") 8 | include_directories ("td/td/generate/") 9 | include_directories($/td/tdutils) 10 | link_directories ("/opt/local/lib/") 11 | 12 | find_package(Lua REQUIRED) 13 | include_directories(${LUA_INCLUDE_DIR}) 14 | 15 | include (CheckIncludeFiles) 16 | check_include_files (execinfo.h HAVE_EXECINFO_H) 17 | if (HAVE_EXECINFO_H) 18 | add_definitions("-DHAVE_EXECINFO_H=1") 19 | endif (HAVE_EXECINFO_H) 20 | 21 | check_include_files (pwd.h HAVE_PWD_H) 22 | if (HAVE_PWD_H) 23 | add_definitions("-DHAVE_PWD_H=1") 24 | endif (HAVE_PWD_H) 25 | 26 | include_directories (${OPENSSL_INCLUDE_DIR}) 27 | 28 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -Wno-deprecated-declarations -Wconversion -Wno-sign-conversion -std=c++14 -fno-omit-frame-pointer") 29 | set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -std=c++14") 30 | 31 | set (TDBOT_SOURCE 32 | main.cpp 33 | cliclient.cpp 34 | clilua.cpp 35 | ) 36 | 37 | 38 | add_executable (telegram-bot ${TDBOT_SOURCE} ${TL_TD_JSON_AUTO} ) 39 | 40 | set_source_files_properties(${TL_TD_JSON_AUTO} PROPERTIES GENERATED TRUE) 41 | add_dependencies(telegram-bot tl_generate_json) 42 | target_link_libraries (telegram-bot tdclient ${ZLIB_LIBRARIES} -lconfig++ ${LUA_LIBRARIES} -lpthread -lcrypto -lssl ) 43 | #target_link_libraries (telegram-curses tdc tdclient ${OPENSSL_LIBRARIES} 44 | # ${ZLIB_LIBRARIES} ${LIBCONFIG_LIBRARY} ${LIBEVENT2_LIBRARY} 45 | # ${LIBEVENT1_LIBRARY} ${LIBJANSSON_LIBRARY} ${LUA_LIBRARIES} -lpthread 46 | # -lpanel -lncursesw -ltermkey) 47 | install (TARGETS telegram-bot 48 | RUNTIME DESTINATION bin) 49 | -------------------------------------------------------------------------------- /cliclient.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #ifdef WIN32 12 | #include 13 | #else 14 | #include 15 | #include 16 | #endif 17 | 18 | #include "telegram.h" 19 | #include "cliclient.hpp" 20 | #include "clilua.hpp" 21 | 22 | #include "td/utils/port/StdStreams.h" 23 | #include "td/utils/Slice.h" 24 | 25 | void set_stdin_echo (bool enable) { 26 | #ifdef WIN32 27 | HANDLE hStdin = GetStdHandle (STD_INPUT_HANDLE); 28 | DWORD mode; 29 | GetConsoleMode (hStdin, &mode); 30 | 31 | if (!enable) { 32 | mode &= ~ENABLE_ECHO_INPUT; 33 | } else { 34 | mode |= ENABLE_ECHO_INPUT; 35 | } 36 | 37 | SetConsoleMode (hStdin, mode); 38 | #else 39 | struct termios tty; 40 | tcgetattr (STDIN_FILENO, &tty); 41 | if (!enable) { 42 | tty.c_lflag &= ~ECHO; 43 | } else { 44 | tty.c_lflag |= ECHO; 45 | } 46 | 47 | (void) tcsetattr(STDIN_FILENO, TCSANOW, &tty); 48 | #endif 49 | } 50 | 51 | 52 | CliClient *CliClient::instance_ = nullptr; 53 | 54 | CliSockFd::CliSockFd(td::SocketFd fd, CliClient *cli) : fd_ (std::move (fd)), cli_ (cli) { 55 | fd_.get_native_fd ().set_is_blocking (false).ensure (); 56 | td::Scheduler::subscribe(fd_.get_poll_info ().extract_pollable_fd (cli_), td::PollFlags::ReadWrite() | td::PollFlags::Close() | td::PollFlags::Error()); 57 | } 58 | 59 | CliSockFd::~CliSockFd() { 60 | close (); 61 | } 62 | 63 | CliStdFd::CliStdFd(CliClient *cli) : cli_ (cli) { 64 | td::Stdin().get_native_fd ().set_is_blocking (false).ensure (); 65 | td::Scheduler::subscribe(td::Stdin ().get_poll_info ().extract_pollable_fd (cli_), td::PollFlags::ReadWrite() | td::PollFlags::Close() | td::PollFlags::Error()); 66 | td::Stdout().get_native_fd ().set_is_blocking (false).ensure (); 67 | td::Scheduler::subscribe(td::Stdout ().get_poll_info ().extract_pollable_fd (cli_), td::PollFlags::Write() | td::PollFlags::Close() | td::PollFlags::Error()); 68 | } 69 | 70 | CliStdFd::~CliStdFd() { 71 | td::Scheduler::unsubscribe(td::Stdin ().get_poll_info ().get_pollable_fd_ref ()); 72 | td::Scheduler::unsubscribe(td::Stdout ().get_poll_info ().get_pollable_fd_ref ()); 73 | } 74 | 75 | void CliFd::work (td::uint64 id) { 76 | sock_sync (); 77 | sock_read (id); 78 | sock_write (id); 79 | sock_close (id); 80 | } 81 | 82 | void CliSockFd::sock_sync () { 83 | td::sync_with_poll (fd_); 84 | } 85 | 86 | void CliStdFd::sock_sync () { 87 | td::sync_with_poll (td::Stdin()); 88 | td::sync_with_poll (td::Stdout()); 89 | } 90 | 91 | void CliSockFd::sock_read (td::uint64 id) { 92 | while (td::can_read_local (fd_)) { 93 | char sb[1024]; 94 | td::MutableSlice s(sb, 1024); 95 | auto res = fd_.read (s); 96 | 97 | if (res.is_ok ()) { 98 | in_ += s.substr (0, res.ok ()).str (); 99 | } 100 | } 101 | 102 | if (in_.length () > 0) { 103 | while (1) { 104 | auto p = in_.find ('\n'); 105 | if (p >= in_.length ()) { 106 | break; 107 | } 108 | auto s = in_.substr (0, p); 109 | if (s.length () > 0) { 110 | cli_->run (id, s); 111 | } 112 | in_.erase (0, p + 1); 113 | } 114 | } 115 | } 116 | 117 | void CliStdFd::sock_read (td::uint64 id) { 118 | while (!half_closed_ && td::can_read_local (td::Stdin())) { 119 | char sb[1024]; 120 | td::MutableSlice s(sb, 1024); 121 | auto res = td::Stdin().read (s); 122 | 123 | if (res.is_ok ()) { 124 | in_ += s.substr (0, res.ok ()).str (); 125 | } 126 | } 127 | 128 | if (in_.length () > 0) { 129 | while (1) { 130 | auto p = in_.find ('\n'); 131 | if (p >= in_.length ()) { 132 | break; 133 | } 134 | auto s = in_.substr (0, p); 135 | in_.erase (0, p + 1); 136 | if (s.length () > 0) { 137 | cli_->run (id, s); 138 | } 139 | } 140 | } 141 | } 142 | 143 | void CliSockFd::sock_write (td::uint64 id) { 144 | while (td::can_write_local (fd_) && out_.length () > 0) { 145 | td::Slice s(out_); 146 | auto res = fd_.write (s); 147 | 148 | if (res.is_ok ()) { 149 | out_.erase (0, res.ok ()); 150 | } 151 | } 152 | } 153 | 154 | void CliStdFd::sock_write (td::uint64 id) { 155 | while (td::can_write_local (td::Stdout()) && out_.length () > 0) { 156 | td::Slice s(out_); 157 | auto res = td::Stdout().write (s); 158 | 159 | if (res.is_ok ()) { 160 | out_.erase (0, res.ok ()); 161 | } 162 | } 163 | } 164 | 165 | void CliSockFd::sock_close (td::uint64 id) { 166 | if (td::can_close_local (fd_)) { 167 | close (); 168 | cli_->del_fd (id); 169 | } 170 | } 171 | 172 | void CliSockFd::close () { 173 | if (!fd_.empty()) { 174 | td::Scheduler::unsubscribe(fd_.get_poll_info ().get_pollable_fd_ref ()); 175 | fd_.close (); 176 | } 177 | } 178 | 179 | 180 | void CliStdFd::sock_close (td::uint64 id) { 181 | if (td::can_close_local (td::Stdin())) { 182 | half_closed_ = true; 183 | } 184 | if (td::can_close_local (td::Stdout())) { 185 | half_closed_ = true; 186 | cli_->del_fd (id); 187 | } 188 | } 189 | 190 | void CliClient::authentificate_restart () { 191 | //send_request (td::make_tl_object(), std::make_unique()); 192 | } 193 | 194 | void CliClient::login_continue (const td::td_api::authorizationStateReady &result) { 195 | std::cout << "logged in successfully\n"; 196 | 197 | if (login_mode_) { 198 | td_.reset(); 199 | close_flag_ = true; 200 | } 201 | } 202 | 203 | void CliClient::login_continue (const td::td_api::authorizationStateWaitPhoneNumber &result) { 204 | if (!login_mode_) { 205 | LOG(FATAL) << "not logged in. Try running with --login option"; 206 | } 207 | if (phone_.length () > 0) { 208 | send_request (td::make_tl_object(phone_, nullptr), std::make_unique()); 209 | } else { 210 | send_request (td::make_tl_object(bot_hash_), std::make_unique()); 211 | } 212 | } 213 | 214 | void CliClient::login_continue (const td::td_api::authorizationStateWaitOtherDeviceConfirmation &result) { 215 | LOG(FATAL) << "unexpected authorization state"; 216 | } 217 | 218 | void CliClient::login_continue (const td::td_api::authorizationStateWaitCode &R) { 219 | if (!login_mode_) { 220 | LOG(FATAL) << "not logged in. Try running with --login option"; 221 | } 222 | 223 | std::cout << "code: "; 224 | set_stdin_echo (false); 225 | std::string code; 226 | std::getline (std::cin, code); 227 | set_stdin_echo (true); 228 | std::cout << "\n"; 229 | 230 | send_request (td::make_tl_object(code), std::make_unique()); 231 | } 232 | 233 | void CliClient::login_continue (const td::td_api::authorizationStateWaitRegistration &R) { 234 | if (!login_mode_) { 235 | LOG(FATAL) << "not logged in. Try running with --login option"; 236 | } 237 | 238 | std::string first_name; 239 | std::string last_name; 240 | 241 | std::cout << "not registered\n"; 242 | std::cout << "first name: "; 243 | std::getline (std::cin, first_name); 244 | std::cout << "last name: "; 245 | std::getline (std::cin, last_name); 246 | 247 | send_request (td::make_tl_object(first_name, last_name), std::make_unique()); 248 | } 249 | 250 | void CliClient::login_continue (const td::td_api::authorizationStateWaitPassword &result) { 251 | if (!login_mode_) { 252 | LOG(FATAL) << "not logged in. Try running with --login option"; 253 | } 254 | std::cout << "password: "; 255 | set_stdin_echo (false); 256 | std::string password; 257 | std::getline (std::cin, password); 258 | set_stdin_echo (true); 259 | std::cout << "\n"; 260 | 261 | send_request (td::make_tl_object(password), std::make_unique()); 262 | } 263 | 264 | void CliClient::login_continue (const td::td_api::authorizationStateLoggingOut &result) { 265 | std::cout << "logging out\n"; 266 | } 267 | 268 | void CliClient::login_continue (const td::td_api::authorizationStateClosing &result) { 269 | std::cout << "closing\n"; 270 | } 271 | 272 | void CliClient::login_continue (const td::td_api::authorizationStateClosed &result) { 273 | std::cout << "closed\n"; 274 | } 275 | 276 | void CliClient::login_continue (const td::td_api::authorizationStateWaitTdlibParameters &result) { 277 | //tdlibParameters use_test_dc:Bool database_directory:string files_directory:string use_file_database:Bool use_chat_info_database:Bool use_message_database:Bool use_secret_chats:Bool api_id:int32 api_hash:string system_language_code:string device_model:string system_version:string application_version:string enable_storage_optimizer:Bool ignore_file_names:Bool = TdlibParameters; 278 | send_request (td::make_tl_object(td::make_tl_object(param_.use_test_dc, param_.database_directory, param_.files_directory, true, true, true, true, param_.api_id, param_.api_hash, "en", "Unix/Console/Bot", "UNIX/??", TELEGRAM_CLI_VERSION, false, param_.ignore_file_names)), std::make_unique()); 279 | } 280 | 281 | void CliClient::login_continue (const td::td_api::authorizationStateWaitEncryptionKey &result) { 282 | send_request (td::make_tl_object(""), std::make_unique()); 283 | } 284 | 285 | void CliClient::authentificate_continue (td::td_api::AuthorizationState &result) { 286 | downcast_call (result, [&](auto &object){this->login_continue (object);}); 287 | } 288 | 289 | void CliClient::on_update (td::tl_object_ptr update) { 290 | if (update->get_id () == td::td_api::updateAuthorizationState::ID) { 291 | auto t = td::move_tl_object_as(update); 292 | authentificate_continue (*t->authorization_state_); 293 | update = td::move_tl_object_as(t); 294 | 295 | } 296 | auto object = td::td_api::move_object_as(update); 297 | std::string v = td::json_encode(td::ToJson (object)); 298 | 299 | fds_.for_each ([&](td::uint64 id, auto &x) { 300 | x.get()->write (v); 301 | x.get()->work (id); 302 | }); 303 | 304 | if (clua_) { 305 | clua_->update (v); 306 | } 307 | } 308 | void CliClient::on_result (td::uint64 id, td::tl_object_ptr result) { 309 | if (id == 0) { 310 | on_update (td::move_tl_object_as(result)); 311 | return; 312 | } 313 | 314 | auto *handler_ptr = handlers_.get(id); 315 | CHECK(handler_ptr != nullptr); 316 | auto handler = std::move(*handler_ptr); 317 | handler->on_result(std::move(result)); 318 | handlers_.erase(id); 319 | } 320 | void CliClient::on_error (td::uint64 id, td::tl_object_ptr error) { 321 | auto *handler_ptr = handlers_.get(id); 322 | CHECK(handler_ptr != nullptr); 323 | auto handler = std::move(*handler_ptr); 324 | handler->on_error(std::move (error)); 325 | handlers_.erase(id); 326 | } 327 | 328 | void CliClient::loop() { 329 | if (!inited_) { 330 | inited_ = true; 331 | init(); 332 | } 333 | 334 | if (port_ > 0) { 335 | td::sync_with_poll (listen_); 336 | while (td::can_read_local (listen_)) { 337 | auto r = listen_.accept (); 338 | if (r.is_ok ()) { 339 | auto x = std::make_unique(r.move_as_ok (), this); 340 | fds_.create (std::move (x)); 341 | LOG(INFO) << "accepted connection\n"; 342 | } 343 | } 344 | if (td::can_close_local (listen_)) { 345 | LOG(FATAL) << "listening socket unexpectedly closed\n"; 346 | } 347 | } 348 | 349 | fds_.for_each ([&](td::uint64 id, auto &x) { 350 | x.get()->work (id); 351 | }); 352 | 353 | if (ready_to_stop_) { 354 | td::Scheduler::instance()->finish(); 355 | stop(); 356 | } 357 | } 358 | 359 | td::unique_ptr CliClient::make_td_callback() { 360 | class TdCallbackImpl : public td::TdCallback { 361 | public: 362 | explicit TdCallbackImpl(CliClient *client) : client_(client) { 363 | } 364 | void on_result(td::uint64 id, td::tl_object_ptr result) override { 365 | client_->on_result(id, std::move(result)); 366 | } 367 | void on_error(td::uint64 id, td::tl_object_ptr error) override { 368 | client_->on_error(id, std::move(error)); 369 | } 370 | ~TdCallbackImpl() override { 371 | client_->on_closed (); 372 | } 373 | 374 | private: 375 | CliClient *client_; 376 | }; 377 | return td::make_unique(this); 378 | } 379 | 380 | void CliClient::init() { 381 | instance_ = this; 382 | init_td(); 383 | 384 | if (!login_mode_) { 385 | auto x = std::make_unique(this); 386 | fds_.create (std::move (x)); 387 | 388 | if (port_ > 0) { 389 | auto r = td::ServerSocketFd::open (port_, addr_); 390 | if (r.is_ok ()) { 391 | listen_ = r.move_as_ok (); 392 | td::Scheduler::subscribe(listen_.get_poll_info ().extract_pollable_fd (this), td::PollFlags::ReadWrite() | td::PollFlags::Close() | td::PollFlags::Error()); 393 | } else { 394 | LOG(FATAL) << "can not initialize listening socket on port " << port_; 395 | } 396 | } 397 | 398 | if (lua_script_.length () > 0) { 399 | clua_ = new CliLua (lua_script_); 400 | } 401 | } 402 | 403 | authentificate_restart (); 404 | } 405 | 406 | void CliClient::tear_down() { 407 | if (!listen_.empty()) { 408 | td::Scheduler::unsubscribe(listen_.get_poll_info ().get_pollable_fd_ref ()); 409 | } 410 | } 411 | -------------------------------------------------------------------------------- /cliclient.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "td/telegram/ClientActor.h" 11 | #include "td/actor/actor.h" 12 | #include "td/tl/TlObject.h" 13 | #include "td/utils/port/ServerSocketFd.h" 14 | #include "td/utils/Container.h" 15 | #include "td/telegram/TdParameters.h" 16 | 17 | #include "auto/td/telegram/td_api.h" 18 | #include "auto/td/telegram/td_api.hpp" 19 | 20 | #include "auto/td/telegram/td_api_json.h" 21 | 22 | 23 | class CliLua; 24 | 25 | class TdQueryCallback { 26 | public: 27 | virtual void on_result(td::tl_object_ptr result) = 0; 28 | virtual void on_error(td::tl_object_ptr error) = 0; 29 | virtual ~TdQueryCallback() { 30 | } 31 | }; 32 | 33 | class CliClient; 34 | 35 | class CliFd { 36 | public: 37 | CliFd() {} 38 | void work(td::uint64 id); 39 | virtual void write(std::string str) = 0; 40 | virtual ~CliFd() = default; 41 | private: 42 | virtual void sock_sync () = 0; 43 | virtual void sock_read (td::uint64 id) = 0; 44 | virtual void sock_write (td::uint64 id) = 0; 45 | virtual void sock_close (td::uint64 id) = 0; 46 | }; 47 | 48 | class CliStdFd : public CliFd { 49 | public: 50 | explicit CliStdFd (CliClient *cli_); 51 | void write(std::string str) override { 52 | out_ += str + "\n"; 53 | } 54 | ~CliStdFd() override; 55 | 56 | private: 57 | void sock_sync () override; 58 | void sock_read (td::uint64 id) override; 59 | void sock_write (td::uint64 id) override; 60 | void sock_close (td::uint64 id) override; 61 | CliClient *cli_; 62 | std::string in_; 63 | std::string out_; 64 | bool half_closed_ = false; 65 | }; 66 | 67 | class CliSockFd : public CliFd { 68 | public: 69 | CliSockFd (td::SocketFd fd_, CliClient *cli_); 70 | void write(std::string str) override { 71 | out_ += str + "\n"; 72 | } 73 | ~CliSockFd() override; 74 | 75 | private: 76 | void sock_sync () override; 77 | void sock_read (td::uint64 id) override; 78 | void sock_write (td::uint64 id) override; 79 | void sock_close (td::uint64 id) override; 80 | void close (); 81 | td::SocketFd fd_; 82 | CliClient *cli_; 83 | std::string in_; 84 | std::string out_; 85 | }; 86 | 87 | class CliClient final : public td::Actor { 88 | public: 89 | explicit CliClient(int port, std::string addr, std::string lua_script, bool login_mode, std::string phone, std::string bot_hash, td::TdParameters param) : port_(port), addr_(addr), lua_script_(lua_script), login_mode_ (login_mode), phone_ (phone), bot_hash_ (bot_hash), param_(param) { 90 | } 91 | 92 | class TdAuthorizationStateCallback : public TdQueryCallback { 93 | void on_result (td::tl_object_ptr result) override { 94 | CHECK (result->get_id () == td::td_api::ok::ID); 95 | } 96 | void on_error (td::tl_object_ptr error) override { 97 | instance_->authentificate_restart (); 98 | } 99 | }; 100 | 101 | class TdCmdCallback : public TdQueryCallback { 102 | void on_result (td::tl_object_ptr result) override { 103 | auto T = cli_->fds_.get (id_); 104 | if (T) { 105 | std::string v = td::json_encode(td::ToJson (result)); 106 | T->get ()->write (v); 107 | T->get ()->work (id_); 108 | } 109 | } 110 | void on_error (td::tl_object_ptr error) override { 111 | on_result (td::move_tl_object_as (error)); 112 | } 113 | 114 | td::uint64 id_; 115 | CliClient *cli_; 116 | 117 | public: 118 | TdCmdCallback(td::uint64 id, CliClient *cli) : id_ (id), cli_ (cli) { 119 | } 120 | 121 | }; 122 | 123 | 124 | void send_request(td::tl_object_ptr f, std::unique_ptr handler) { 125 | auto id = handlers_.create(std::move(handler)); 126 | if (!td_.empty()) { 127 | send_closure(td_, &td::ClientActor::request, id, std::move(f)); 128 | } else { 129 | LOG(ERROR) << "Failed to send: " << td::td_api::to_string(f); 130 | } 131 | }; 132 | 133 | static CliClient *instance_; 134 | 135 | void del_fd (td::uint64 id) { 136 | fds_.erase (id); 137 | } 138 | 139 | void run (td::uint64 id, std::string cmd) { 140 | while (cmd.length () > 0 && isspace (cmd[0])) { 141 | cmd = cmd.substr (1); 142 | } 143 | while (cmd.length () > 0 && isspace (cmd[cmd.length () - 1])) { 144 | cmd = cmd.substr (0, cmd.length () - 1); 145 | } 146 | auto res = td::json_decode (cmd); 147 | 148 | if (res.is_ok ()) { 149 | td::tl_object_ptr object; 150 | 151 | auto r = from_json(object, res.move_as_ok ()); 152 | 153 | if (r.is_ok ()) { 154 | send_request(std::move (object), std::make_unique(id,this)); 155 | return; 156 | } else { 157 | auto R = r.move_as_error (); 158 | 159 | std::string er = std::string ("") + "{\"_\":\"error\",\"code\":" + std::to_string (R.code ()) + ",\"message\":\"" + R.public_message () + "\"}"; 160 | 161 | if (fds_.get (id)) { 162 | fds_.get (id)->get ()->write (er); 163 | } 164 | return; 165 | } 166 | } 167 | 168 | 169 | auto R = res.move_as_error (); 170 | 171 | std::string er = std::string ("") + "{\"_\":\"error\",\"code\":" + std::to_string (R.code ()) + ",\"message\":\"" + R.public_message () + "\"}"; 172 | 173 | if (fds_.get (id)) { 174 | fds_.get (id)->get ()->write (er); 175 | } 176 | } 177 | 178 | private: 179 | void authentificate_restart (); 180 | void authentificate_continue (td::td_api::AuthorizationState &state); 181 | void login_continue (const td::td_api::authorizationStateReady &result); 182 | void login_continue (const td::td_api::authorizationStateWaitTdlibParameters &result); 183 | void login_continue (const td::td_api::authorizationStateWaitPhoneNumber &result); 184 | void login_continue (const td::td_api::authorizationStateWaitOtherDeviceConfirmation &result); 185 | void login_continue (const td::td_api::authorizationStateWaitCode &result); 186 | void login_continue (const td::td_api::authorizationStateWaitRegistration &result); 187 | void login_continue (const td::td_api::authorizationStateWaitPassword &result); 188 | void login_continue (const td::td_api::authorizationStateLoggingOut &result); 189 | void login_continue (const td::td_api::authorizationStateWaitEncryptionKey &result); 190 | void login_continue (const td::td_api::authorizationStateClosing &result); 191 | void login_continue (const td::td_api::authorizationStateClosed &result); 192 | 193 | void start_up() override { 194 | yield(); 195 | } 196 | 197 | void tear_down() override; 198 | 199 | void on_update (td::tl_object_ptr update); 200 | void on_result (td::uint64 id, td::tl_object_ptr result); 201 | void on_error (td::uint64 id, td::tl_object_ptr error); 202 | 203 | void on_before_close() { 204 | td_.reset(); 205 | } 206 | 207 | void on_closed() { 208 | LOG(INFO) << "on_closed"; 209 | if (close_flag_) { 210 | ready_to_stop_ = true; 211 | yield(); 212 | return; 213 | } 214 | init_td(); 215 | } 216 | 217 | 218 | td::unique_ptr make_td_callback(); 219 | 220 | void init_td() { 221 | td_ = td::create_actor(td::Slice ("TDPROXY"), make_td_callback()); 222 | } 223 | 224 | void init (); 225 | 226 | 227 | bool inited_ = false; 228 | void loop() override; 229 | 230 | 231 | void timeout_expired() override { 232 | } 233 | 234 | /*void add_cmd(std::string cmd) { 235 | cmd_queue_.push(cmd); 236 | }*/ 237 | 238 | //td::int32 my_id_ = 0; 239 | 240 | int port_; 241 | std::string addr_; 242 | CliLua *clua_ = nullptr; 243 | std::string lua_script_ = ""; 244 | bool login_mode_ = false; 245 | std::string phone_; 246 | std::string bot_hash_; 247 | 248 | td::TdParameters param_; 249 | td::ActorOwn td_; 250 | //std::queue cmd_queue_; 251 | bool close_flag_ = false; 252 | bool ready_to_stop_ = false; 253 | td::ServerSocketFd listen_; 254 | 255 | td::Container> fds_; 256 | td::Container> handlers_; 257 | }; 258 | -------------------------------------------------------------------------------- /clilua.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "clilua.hpp" 4 | #include "json.hpp" 5 | 6 | CliLua *CliLua::instance_ = nullptr; 7 | 8 | using json = nlohmann::json; 9 | 10 | void parse_json (lua_State *L, json &res) { 11 | if (lua_isboolean (L, -1)) { 12 | res = static_cast(lua_toboolean (L, -1)); 13 | return; 14 | } else if (lua_type (L, -1) == LUA_TNUMBER) { 15 | auto x = lua_tonumber (L, -1); 16 | auto x64 = static_cast(x); 17 | if (x == static_cast(x64)) { 18 | res = x64; 19 | } else { 20 | res = x; 21 | } 22 | return; 23 | } else if (lua_isstring (L, -1)) { 24 | size_t len; 25 | const char *s = lua_tolstring (L, -1, &len); 26 | res = std::string (s, len); 27 | return; 28 | } else if (lua_istable (L, -1)) { 29 | 30 | bool arr = true; 31 | int size = 0; 32 | 33 | lua_pushnil (L); 34 | while (lua_next (L, -2)) { 35 | if (!(arr && lua_type (L, -2) == LUA_TNUMBER && lua_tointeger (L, -2) == size)) { 36 | arr = false; 37 | } 38 | size ++; 39 | lua_pop (L, 1); 40 | } 41 | 42 | if (arr) { 43 | res = json::array (); 44 | } else { 45 | res = json::object (); 46 | } 47 | 48 | lua_pushnil (L); 49 | while (lua_next (L, -2)) { 50 | if (arr) { 51 | int x = (int)lua_tointeger (L, -2); 52 | parse_json (L, res[x]); 53 | lua_pop (L, 1); 54 | } else { 55 | size_t len; 56 | 57 | if (lua_type (L, -2) == LUA_TNUMBER) { 58 | auto x = lua_tointeger (L, -2); 59 | std::string k = std::to_string (x); 60 | parse_json (L, res[k]); 61 | } else { 62 | const char *key = lua_tolstring (L, -2, &len); 63 | std::string k = std::string (key, len); 64 | parse_json (L, res[k]); 65 | } 66 | lua_pop (L, 1); 67 | } 68 | } 69 | } else { 70 | res = false; 71 | return; 72 | } 73 | } 74 | 75 | void push_json (lua_State *L, json &j) { 76 | if (j.is_null ()) { 77 | lua_pushnil (L); 78 | } else if (j.is_boolean ()) { 79 | lua_pushboolean (L, j.get()); 80 | } else if (j.is_string ()) { 81 | auto s = j.get(); 82 | lua_pushlstring (L, s.c_str (), s.length ()); 83 | } else if (j.is_number_integer ()) { 84 | auto v = j.get(); 85 | 86 | if (v == static_cast(v)) { 87 | lua_pushnumber (L, static_cast(v)); 88 | } else { 89 | std::string s = std::to_string (v); 90 | lua_pushlstring (L, s.c_str (), s.length ()); 91 | } 92 | } else if (j.is_number_float ()) { 93 | auto v = j.get(); 94 | lua_pushnumber (L, v); 95 | } else if (j.is_array ()) { 96 | lua_newtable (L); 97 | int p = 0; 98 | for (auto it = j.begin (); it != j.end (); it ++, p ++) { 99 | lua_pushnumber (L, p); 100 | push_json (L, *it); 101 | lua_settable (L, -3); 102 | } 103 | } else if (j.is_object ()) { 104 | lua_newtable (L); 105 | for (auto it = j.begin (); it != j.end (); it ++) { 106 | auto s = it.key (); 107 | 108 | lua_pushlstring (L, s.c_str (), s.length ()); 109 | push_json (L, it.value ()); 110 | lua_settable (L, -3); 111 | } 112 | } else { 113 | lua_pushnil (L); 114 | } 115 | } 116 | 117 | int lua_parse_function (lua_State *L) { 118 | if (lua_gettop (L) != 3) { 119 | lua_pushboolean (L, 0); 120 | return 1; 121 | } 122 | 123 | int a1 = luaL_ref (L, LUA_REGISTRYINDEX); 124 | int a2 = luaL_ref (L, LUA_REGISTRYINDEX); 125 | 126 | json j; 127 | parse_json (L, j); 128 | lua_pop (L, 1); 129 | 130 | auto cmd = j.dump (); 131 | 132 | LOG(INFO) << cmd << "\n"; 133 | 134 | auto res = td::json_decode (cmd); 135 | 136 | if (res.is_ok ()) { 137 | td::tl_object_ptr object; 138 | 139 | auto r = from_json(object, res.move_as_ok ()); 140 | 141 | if (r.is_ok ()) { 142 | CliClient::instance_->send_request(std::move (object), std::make_unique(a1, a2, CliLua::instance_)); 143 | lua_pushboolean (L, 1); 144 | return 1; 145 | } else { 146 | LOG(ERROR) << "FAILED TO PARSE LUA: " << r.move_as_error () << "\n"; 147 | lua_pushboolean (L, 0); 148 | return 1; 149 | } 150 | } 151 | 152 | LOG(ERROR) << "FAILED TO PARSE LUA: " << res.move_as_error () << "\n"; 153 | 154 | lua_pushboolean (L, 0); 155 | return 1; 156 | } 157 | 158 | CliLua::CliLua (std::string file) { 159 | instance_ = this; 160 | 161 | luaState_ = luaL_newstate (); 162 | luaL_openlibs (luaState_); 163 | 164 | lua_register (luaState_, "tdbot_function", lua_parse_function); 165 | 166 | int r = luaL_dofile (luaState_, file.c_str ()); 167 | 168 | if (r) { 169 | LOG(FATAL) << "lua: " << lua_tostring (luaState_, -1) << "\n"; 170 | } 171 | } 172 | 173 | void CliLua::update (std::string update) { 174 | auto j = json::parse (update); 175 | 176 | lua_settop (luaState_, 0); 177 | lua_getglobal (luaState_, "tdbot_update_callback"); 178 | 179 | push_json (luaState_, j); 180 | 181 | int r = lua_pcall (luaState_, 1, 0, 0); 182 | 183 | if (r) { 184 | LOG(FATAL) << "lua: " << lua_tostring (luaState_, -1) << "\n"; 185 | } 186 | 187 | } 188 | 189 | void TdLuaCallback::on_result (td::tl_object_ptr result) { 190 | std::string v = td::json_encode(td::ToJson (result)); 191 | 192 | clua_->result (v, a1_, a2_); 193 | } 194 | 195 | void CliLua::result (std::string update, int a1, int a2) { 196 | auto j = json::parse (update); 197 | 198 | lua_settop (luaState_, 0); 199 | 200 | lua_rawgeti (luaState_, LUA_REGISTRYINDEX, a2); 201 | lua_rawgeti (luaState_, LUA_REGISTRYINDEX, a1); 202 | 203 | push_json (luaState_, j); 204 | 205 | int r = lua_pcall (luaState_, 2, 0, 0); 206 | 207 | luaL_unref (luaState_, LUA_REGISTRYINDEX, a1); 208 | luaL_unref (luaState_, LUA_REGISTRYINDEX, a2); 209 | 210 | if (r) { 211 | LOG(FATAL) << "lua: " << lua_tostring (luaState_, -1) << "\n"; 212 | } 213 | } 214 | 215 | -------------------------------------------------------------------------------- /clilua.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cliclient.hpp" 4 | 5 | #include 6 | #include 7 | 8 | class CliClient; 9 | 10 | class CliLua { 11 | public: 12 | CliLua (std::string file); 13 | void update(std::string upd); 14 | void result(std::string result, int a1, int a2); 15 | static CliLua *instance_; 16 | private: 17 | lua_State *luaState_; 18 | }; 19 | 20 | class TdLuaCallback : public TdQueryCallback { 21 | void on_result (td::tl_object_ptr result) override; 22 | void on_error (td::tl_object_ptr error) override { 23 | on_result (td::move_tl_object_as (error)); 24 | } 25 | 26 | int a1_, a2_; 27 | CliLua *clua_; 28 | 29 | public: 30 | TdLuaCallback(int a1, int a2, CliLua *clua) : a1_(a1), a2_(a2), clua_ (clua) { 31 | } 32 | 33 | }; 34 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #if HAVE_EXECINFO_H 7 | #include 8 | #endif 9 | #if HAVE_PWD_H 10 | #include 11 | #include 12 | #endif 13 | #include "td/telegram/TdParameters.h" 14 | #include "td/utils/FileLog.h" 15 | #include "td/actor/actor.h" 16 | #include "td/actor/ConcurrentScheduler.h" 17 | #include "td/utils/port/signals.h" 18 | #include "td/utils/port/path.h" 19 | 20 | #include "telegram.h" 21 | #include "cliclient.hpp" 22 | #include "lua.h" 23 | 24 | #define CONFIG_DIRECTORY ".telegram-bot" 25 | #define CONFIG_DIRECTORY_MODE 0700 26 | 27 | std::string config_filename; 28 | std::string profile; 29 | std::string logname; 30 | std::string lua_script; 31 | std::string phone; 32 | std::string bot_hash; 33 | std::string username; 34 | std::string groupname; 35 | std::string unix_socket; 36 | std::string program; 37 | 38 | bool login_mode; 39 | bool daemonize; 40 | bool accept_any_tcp_connections; 41 | 42 | int verbosity; 43 | int sfd = -1; 44 | int usfd = -1; 45 | int port = -1; 46 | 47 | td::TdParameters param; 48 | 49 | std::string get_home_directory () /* {{{ */ { 50 | auto str = getenv ("TELEGRAM_BOT_HOME"); 51 | if (str && std::strlen (str)) { 52 | return str; 53 | } 54 | str = getenv ("HOME"); 55 | if (str && std::strlen (str)) { 56 | return str; 57 | } 58 | 59 | #if HAVE_PWD_H 60 | setpwent (); 61 | auto user_id = getuid (); 62 | while (1) { 63 | auto current_passwd = getpwent (); 64 | if (!current_passwd) { 65 | break; 66 | } 67 | if (current_passwd->pw_uid == user_id) { 68 | endpwent (); 69 | return current_passwd->pw_dir; 70 | } 71 | } 72 | endpwent (); 73 | #endif 74 | 75 | return "."; 76 | } 77 | /* }}} */ 78 | 79 | std::string get_config_directory () /* {{{ */ { 80 | auto str = getenv ("TELEGRAM_CONFIG_DIR"); 81 | if (str && std::strlen (str)) { 82 | return str; 83 | } 84 | 85 | // XDG: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html 86 | str = getenv ("XDG_CONFIG_HOME"); 87 | if (str && std::strlen (str)) { 88 | return std::string (str) + "/" + CONFIG_DIRECTORY; 89 | } 90 | 91 | return get_home_directory () + "/" + CONFIG_DIRECTORY; 92 | } 93 | /* }}} */ 94 | 95 | void parse_config () /* {{{ */ { 96 | //config_filename = make_full_path (config_filename); 97 | /// Is test Telegram environment should be used instead of the production environment. 98 | param.use_test_dc = false; 99 | /// Application identifier for Telegram API access, can be obtained at https://my.telegram.org. 100 | param.api_id = TELEGRAM_CLI_API_ID; 101 | /// Application identifier hash for Telegram API access, can be obtained at https://my.telegram.org. 102 | param.api_hash = TELEGRAM_CLI_API_HASH; 103 | /// IETF language tag of users language. 104 | //param.language_code = "en"; 105 | /// Model of a device application is run on. 106 | //param.device_model = "Console"; 107 | /// Version of an operating system application is run on. 108 | //param.system_version = "UNIX/XX"; 109 | /// Application version. 110 | //param.app_version = TELEGRAM_CLI_VERSION; 111 | /// If set to false, information about files will be lost on application restart. 112 | param.use_file_db = true; 113 | /// If set to true, old files will be automatically deleted. 114 | //param.use_file_gc = false; 115 | param.ignore_file_names = false; 116 | /// By default, secret chats support is disabled. Set to true to enable secret chats support. 117 | param.use_secret_chats = true; 118 | /// If set to true, the library will maintain cache of users, groups, channels and secret chats. Implies use_file_db. 119 | param.use_chat_info_db = true; 120 | /// If set to true, the library will maintain cache of chats and messages. Implies use_chat_info_db. 121 | param.use_message_db = true; 122 | 123 | bool default_config = false; 124 | if (config_filename.length () <= 0) { 125 | config_filename = "config"; 126 | default_config = true; 127 | } 128 | if (config_filename[0] != '/') { 129 | config_filename = get_config_directory () + "/" + config_filename; 130 | } 131 | 132 | if (default_config) { 133 | td::mkdir (get_config_directory ()).ensure (); 134 | } 135 | 136 | libconfig::Config conf; 137 | 138 | try { 139 | conf.readFile (config_filename.c_str ()); 140 | } catch (const libconfig::FileIOException &e) { 141 | if (!default_config) { 142 | std::cerr << "can not open config file '" << config_filename << "'\n"; 143 | std::cerr << "diagnosis: " << e.what () << "\n"; 144 | 145 | std::exit (EXIT_FAILURE); 146 | } else { 147 | std::cout << "config '" << config_filename << "' not found. Using default config\n"; 148 | } 149 | } catch (const libconfig::ParseException &e) { 150 | std::cerr << "can not parse config file at " << e.getFile () << ":" << std::to_string (e.getLine ()) << "\n"; 151 | std::cerr << "diagnosis: " << e.getError () << "\n"; 152 | 153 | std::exit (EXIT_FAILURE); 154 | } 155 | 156 | if (profile.length () == 0) { 157 | try { 158 | conf.lookupValue ("default_profile", profile); 159 | } catch (const libconfig::SettingNotFoundException &) {} 160 | } 161 | 162 | std::string prefix; 163 | if (profile.length () > 0) { 164 | prefix = profile + "."; 165 | } else { 166 | prefix = ""; 167 | } 168 | 169 | std::string config_directory; 170 | if (profile.length () > 0) { 171 | config_directory = get_config_directory () + "/" + profile + "/"; 172 | } else { 173 | config_directory = get_config_directory () + "/"; 174 | } 175 | 176 | try { 177 | bool test_mode = false; 178 | conf.lookupValue (prefix + "test", test_mode); 179 | param.use_test_dc = test_mode; 180 | } catch (const libconfig::SettingNotFoundException &) {} 181 | 182 | try { 183 | std::string s; 184 | conf.lookupValue (prefix + "config_directory", s); 185 | if (s.length () > 0 && s[0] == '/') { 186 | config_directory = s + "/"; 187 | } else if (s.length () > 0) { 188 | config_directory = get_config_directory () + "/" + s + "/"; 189 | } 190 | } catch (const libconfig::SettingNotFoundException &) {} 191 | 192 | //try { 193 | // conf.lookupValue (prefix + "language_code", param.language_code); 194 | //} catch (const libconfig::SettingNotFoundException &) {} 195 | 196 | try { 197 | conf.lookupValue (prefix + "use_file_db", param.use_file_db); 198 | } catch (const libconfig::SettingNotFoundException &) {} 199 | 200 | //try { 201 | // conf.lookupValue (prefix + "use_file_gc", param.use_file_gc); 202 | //} catch (const libconfig::SettingNotFoundException &) {} 203 | 204 | //try { 205 | // conf.lookupValue (prefix + "file_readable_names", param.file_readable_names); 206 | //} catch (const libconfig::SettingNotFoundException &) {} 207 | 208 | try { 209 | conf.lookupValue (prefix + "use_secret_chats", param.use_secret_chats); 210 | } catch (const libconfig::SettingNotFoundException &) {} 211 | 212 | try { 213 | conf.lookupValue (prefix + "use_chat_info_db", param.use_chat_info_db); 214 | } catch (const libconfig::SettingNotFoundException &) {} 215 | 216 | try { 217 | conf.lookupValue (prefix + "use_message_db", param.use_chat_info_db); 218 | } catch (const libconfig::SettingNotFoundException &) {} 219 | 220 | try { 221 | conf.lookupValue (prefix + "logname", logname); 222 | if (logname.length () > 0 && logname[0] != '/') { 223 | logname = config_directory + logname; 224 | } 225 | } catch (const libconfig::SettingNotFoundException &) {} 226 | 227 | try { 228 | conf.lookupValue (prefix + "verbosity", verbosity); 229 | } catch (const libconfig::SettingNotFoundException &) {} 230 | 231 | try { 232 | conf.lookupValue (prefix + "lua_script", lua_script); 233 | if (lua_script.length () > 0 && lua_script[0] != '/') { 234 | lua_script = config_directory + lua_script; 235 | } 236 | } catch (const libconfig::SettingNotFoundException &) {} 237 | 238 | std::cout << config_directory << "\n"; 239 | param.database_directory = config_directory + "/data"; 240 | param.files_directory = config_directory + "/files"; 241 | 242 | td::mkdir (config_directory, CONFIG_DIRECTORY_MODE).ensure (); 243 | } 244 | /* }}} */ 245 | 246 | void usage () /* {{{ */ { 247 | std::cout 248 | << "Usage: \n" 249 | << " --verbosity/-v increase verbosity (0-ERROR 1-WARNIN 2-NOTICE 3+-DEBUG-levels)\n" 250 | << " --config/-c config file name\n" 251 | << " --profile/-p use specified profile\n" 252 | << " --daemonize/-d daemon mode\n" 253 | << " --logname/-L log file name\n" 254 | << " --username/-U change uid after start\n" 255 | << " --groupname/-G change gid after start\n" 256 | << " --tcp-port/-P port to listen for input commands\n" 257 | << " --unix-socket/-S unix socket to create\n" 258 | << " --exec/-e make commands end exit\n" 259 | << " --help/-h prints this help\n" 260 | << " --accept-any-tcp accepts tcp connections from any src (only loopback by default)\n" 261 | << " --bot/-b bot mode\n" 262 | << " --phone/-u specify username (would not be asked during authorization)\n" 263 | << " --login start in login mode\n" 264 | ; 265 | 266 | std::exit (1); 267 | } 268 | /* }}} */ 269 | 270 | void args_parse (int argc, char *argv[]) { 271 | static struct option long_options[] = { 272 | {"verbosity", no_argument, 0, 'v'}, 273 | {"config", required_argument, 0, 'c'}, 274 | {"profile", required_argument, 0, 'p'}, 275 | {"lua-script", required_argument, 0, 's'}, 276 | {"daemonize", no_argument, 0, 'd'}, 277 | {"logname", required_argument, 0, 'L'}, 278 | {"username", required_argument, 0, 'U'}, 279 | {"groupname", required_argument, 0, 'G'}, 280 | {"tcp-port", required_argument, 0, 'P'}, 281 | {"unix-socket", required_argument, 0, 'S'}, 282 | {"exec", required_argument, 0, 'e'}, 283 | {"help", no_argument, 0, 'h'}, 284 | {"bot", required_argument, 0, 'b'}, 285 | {"phone", required_argument, 0, 'u'}, 286 | {"accept-any-tcp", no_argument, 0, 1001}, 287 | {"login", no_argument, 0, 1002}, 288 | {0, 0, 0, 0 } 289 | }; 290 | 291 | 292 | int opt = 0; 293 | while ((opt = getopt_long (argc, argv, "vc:p:s:dL:U:G:P:S:e:hb:u:" 294 | , long_options, NULL 295 | )) != -1) { 296 | switch (opt) { 297 | case 'v': 298 | verbosity ++; 299 | break; 300 | case 'c': 301 | config_filename = optarg; 302 | break; 303 | case 'p': 304 | profile = optarg; 305 | break; 306 | case 's': 307 | lua_script = optarg; 308 | break; 309 | case 'd': 310 | daemonize = true; 311 | break; 312 | case 'L': 313 | logname = optarg; 314 | break; 315 | case 'U': 316 | username = optarg; 317 | break; 318 | case 'G': 319 | groupname = optarg; 320 | break; 321 | case 'P': 322 | port = atoi (optarg); 323 | break; 324 | case 'S': 325 | unix_socket = optarg; 326 | break; 327 | case 'e': 328 | program = optarg; 329 | break; 330 | case 'h': 331 | usage (); 332 | break; 333 | case 'b': 334 | if (bot_hash.length () > 0 || phone.length () > 0) { 335 | std::cout << "should have at most one option of --bot and --phone\n"; 336 | usage (); 337 | } 338 | bot_hash = optarg; 339 | break; 340 | case 'u': 341 | if (bot_hash.length () > 0 || phone.length () > 0) { 342 | std::cout << "should have at most one option of --bot and --phone\n"; 343 | usage (); 344 | } 345 | phone = optarg; 346 | break; 347 | case 1001: 348 | accept_any_tcp_connections = true; 349 | break; 350 | case 1002: 351 | login_mode = true; 352 | break; 353 | default: 354 | usage (); 355 | break; 356 | } 357 | } 358 | } 359 | 360 | void termination_signal_handler (int signum) { 361 | td::signal_safe_write_signal_number (signum); 362 | 363 | #if HAVE_EXECINFO_H 364 | void *buffer[255]; 365 | const int calls = backtrace (buffer, sizeof (buffer) / sizeof (void *)); 366 | backtrace_symbols_fd (buffer, calls, 1); 367 | #endif 368 | 369 | std::_Exit (EXIT_FAILURE); 370 | } 371 | 372 | void main_loop() { 373 | if (logname.length () > 0) { 374 | static td::FileLog file_log; 375 | file_log.init (logname); 376 | td::log_interface = &file_log; 377 | } 378 | 379 | SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL) + verbosity); 380 | 381 | td::ConcurrentScheduler scheduler; 382 | scheduler.init(4); 383 | 384 | scheduler.create_actor_unsafe(0, "CliClient", port, accept_any_tcp_connections ? "0.0.0.0" : "127.0.0.1", lua_script, login_mode, phone, bot_hash, param).release(); 385 | 386 | scheduler.start(); 387 | while (scheduler.run_main(100)) { 388 | } 389 | scheduler.finish(); 390 | } 391 | 392 | int main (int argc, char *argv[]) { 393 | SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL)); 394 | 395 | td::setup_signals_alt_stack ().ensure (); 396 | td::set_signal_handler (td::SignalType::Abort, termination_signal_handler).ensure (); 397 | td::set_signal_handler (td::SignalType::Error, termination_signal_handler).ensure (); 398 | td::set_signal_handler (td::SignalType::Quit, termination_signal_handler).ensure (); 399 | td::ignore_signal (td::SignalType::Pipe).ensure (); 400 | 401 | args_parse (argc, argv); 402 | parse_config (); 403 | 404 | if (login_mode) { 405 | if (phone.length () <= 0 && bot_hash.length () <= 0) { 406 | std::cout << "in login mode need exactly one of phone and bot_hash\n"; 407 | usage (); 408 | } 409 | } 410 | 411 | main_loop (); 412 | 413 | return 0; 414 | } 415 | -------------------------------------------------------------------------------- /telegram.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of telegram-cli. 3 | 4 | Telegram-cli is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | Telegram-cli is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this telegram-cli. If not, see . 16 | 17 | Copyright Vitaly Valtman 2013-2015 18 | */ 19 | 20 | #pragma once 21 | 22 | #ifndef PROG_NAME 23 | #define PROG_NAME "telegram-bot" 24 | #endif 25 | 26 | #define TELEGRAM_CLI_VERSION "1.6.3" 27 | #define TELEGRAM_CLI_API_HASH "36722c72256a24c1225de00eb6a1ca74" 28 | #define TELEGRAM_CLI_API_ID 2899 29 | 30 | #define TELEGRAM_CLI_VERSION_STR \ 31 | "Telegram-bot version " TELEGRAM_CLI_VERSION " (uses tdlib)" 32 | --------------------------------------------------------------------------------