// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include "td/telegram/ClientActor.h" #include "td/telegram/Log.h" #include "td/telegram/td_api_json.h" #include "td/actor/actor.h" #include "td/tl/tl_json.h" // should be included after td_api_json? #include "memprof/memprof.h" #include "td/utils/buffer.h" #include "td/utils/BufferedFd.h" #include "td/utils/FileLog.h" #include "td/utils/format.h" #include "td/utils/JsonBuilder.h" #include "td/utils/logging.h" #include "td/utils/misc.h" #include "td/utils/port/Fd.h" #include "td/utils/port/FileFd.h" #include "td/utils/port/signals.h" #include "td/utils/port/Stat.h" #include "td/utils/port/thread_local.h" #include "td/utils/Random.h" #include "td/utils/ScopeGuard.h" #include "td/utils/Slice.h" #include "td/utils/Status.h" #include "td/utils/StringBuilder.h" #include "td/utils/Time.h" #ifndef USE_READLINE #include "td/utils/find_boundary.h" #endif #include #include #include #include #include #include // for strcmp #include #include #include #include #include #include #include #include #ifdef USE_READLINE /* Standard readline include files. */ #include #include #endif namespace td { static void dump_memory_usage() { if (is_memprof_on()) { LOG(WARNING) << "memory_dump"; clear_thread_locals(); std::vector v; dump_alloc([&](const AllocInfo &info) { v.push_back(info); }); std::sort(v.begin(), v.end(), [](const AllocInfo &a, const AllocInfo &b) { return a.size > b.size; }); size_t total_size = 0; size_t other_size = 0; int cnt = 0; for (auto &info : v) { if (cnt++ < 50) { LOG(WARNING) << td::format::as_size(info.size) << td::format::as_array(info.backtrace); } else { other_size += info.size; } total_size += info.size; } LOG(WARNING) << tag("other", td::format::as_size(other_size)); LOG(WARNING) << tag("total", td::format::as_size(total_size)); LOG(WARNING) << tag("total traces", get_ht_size()); LOG(WARNING) << tag("fast_backtrace_success_rate", get_fast_backtrace_success_rate()); } } #ifdef USE_READLINE const char *prompt = "td_cli> "; static int32 saved_point; static string saved_line; static std::atomic_flag readline_lock = ATOMIC_FLAG_INIT; static void deactivate_readline() { while (readline_lock.test_and_set(std::memory_order_acquire)) { // spin } saved_point = rl_point; saved_line = string(rl_line_buffer, rl_end); rl_set_prompt(""); rl_replace_line("", 0); rl_redisplay(); } static void reactivate_readline() { rl_set_prompt(prompt); rl_replace_line(saved_line.c_str(), 0); rl_point = saved_point; rl_redisplay(); readline_lock.clear(std::memory_order_release); } static char *command_generator(const char *text, int state) { static vector commands{"GetContacts", "GetChats", "GetHistory", "SetVerbosity", "SendVideo", "SearchDocument", "GetChatMember", "GetSupergroupAdministrators", "GetSupergroupBanned", "GetSupergroupMembers", "GetFile", "DownloadFile", "CancelDownloadFile", "ImportContacts", "RemoveContacts", "DumpNetQueries", "CreateSecretChat", "CreateNewSecretChat"}; static size_t cmd_i; if (state == 0) { cmd_i = 0; } while (cmd_i < commands.size()) { const char *a = commands[cmd_i++].c_str(); const char *b = text; const char *c = b; while (*c && to_lower(*c) == *c) { c++; } bool only_lowercase = !is_alpha(*c); while (*a && *b) { if (*a == *b || (only_lowercase && *a == to_upper(*b))) { b++; } a++; } if (*b == 0) { // TODO call to strdup is completely wrong. Readline will try to call std::free() on the returned char*, // which may be incompatible with the std::malloc() called by strdup // It is especially likely to happen if Readline is used as dynamic library // Unfortunately Readline doesn't provide memory allocation functions/memory deallocation callbacks to fix this #if TD_MSVC return _strdup(commands[cmd_i - 1].c_str()); #else return strdup(commands[cmd_i - 1].c_str()); #endif } } return nullptr; } static char **tg_cli_completion(const char *text, int start, int end) { char **matches = nullptr; if (start == 0) { matches = rl_completion_matches(text, command_generator); } return matches; } #endif class CliLog : public LogInterface { public: void append(CSlice slice, int log_level) override { #ifdef USE_READLINE deactivate_readline(); #endif if (log_level == VERBOSITY_NAME(PLAIN)) { #if TD_WINDOWS TsCerr() << slice; #else TsCerr() << TC_GREEN << slice << TC_EMPTY; #endif } else { default_log_interface->append(slice, log_level); } #ifdef USE_READLINE reactivate_readline(); #endif } void rotate() override { } }; struct SendMessageInfo { double start_time = 0; double quick_ack_time = 0; double ack_time = 0; bool empty() const { return quick_ack_time != 0 || ack_time != 0; } }; StringBuilder &operator<<(StringBuilder &sb, const SendMessageInfo &info) { sb << format::cond(info.quick_ack_time != 0, tag("quick_ack", info.quick_ack_time - info.start_time)); sb << format::cond(info.ack_time != 0, tag("ack", info.ack_time - info.start_time)); return sb; } class CliClient final : public Actor { public: CliClient(bool use_test_dc, bool get_chat_list, bool disable_network, int32 api_id, string api_hash) : use_test_dc_(use_test_dc) , get_chat_list_(get_chat_list) , disable_network_(disable_network) , api_id_(api_id) , api_hash_(api_hash) { } static void quit_instance() { instance_->quit(); } private: void start_up() override { yield(); } std::unordered_map query_id_to_send_message_info_; std::unordered_map message_id_to_send_message_info_; struct User { string first_name; string last_name; string username; }; std::unordered_map users_; std::unordered_map username_to_user_id_; void register_user(const td_api::user &user) { User &new_user = users_[user.id_]; new_user.first_name = user.first_name_; new_user.last_name = user.last_name_; new_user.username = user.username_; username_to_user_id_[to_lower(new_user.username)] = user.id_; } void print_user(Logger &log, int32 user_id, bool full = false) { const User *user = &users_[user_id]; log << user->first_name << " " << user->last_name << " #" << user_id; if (!user->username.empty()) { log << " @" << user->username; } } void update_users(const td_api::users &users) { Logger log{*log_interface, VERBOSITY_NAME(PLAIN)}; for (auto &user_id : users.user_ids_) { if (user_id == 0) { continue; } print_user(log, user_id); log << "\n"; } } std::unordered_map username_to_supergroup_id; void register_supergroup(const td_api::supergroup &supergroup) { if (!supergroup.username_.empty()) { username_to_supergroup_id[to_lower(supergroup.username_)] = supergroup.id_; } } void update_option(const td_api::updateOption &option) { if (option.name_ == "my_id") { if (option.value_->get_id() == td_api::optionValueInteger::ID) { my_id_ = static_cast(option.value_.get())->value_; LOG(INFO) << "Set my id to " << my_id_; } } } int64 get_history_chat_id = 0; int64 search_chat_id = 0; void on_get_messages(const td_api::messages &messages) { if (get_history_chat_id != 0) { int64 last_message_id = 0; for (auto &m : messages.messages_) { // LOG(PLAIN) << to_string(m); if (m->content_->get_id() == td_api::messageText::ID) { LOG(PLAIN) << td::oneline(static_cast(m->content_.get())->text_->text_) << "\n"; } last_message_id = m->id_; } if (last_message_id > 0) { send_request(make_tl_object(get_history_chat_id, last_message_id, 0, 100, false)); } else { get_history_chat_id = 0; } } if (search_chat_id != 0) { if (!messages.messages_.empty()) { auto last_message_id = messages.messages_.back()->id_; LOG(ERROR) << (last_message_id >> 20); send_request( make_tl_object(search_chat_id, "", 0, last_message_id, 0, 100, make_tl_object())); } else { search_chat_id = 0; } } } void on_get_message(const td_api::message &message) { if (message.sending_state_ != nullptr && message.sending_state_->get_id() == td_api::messageSendingStatePending::ID) { // send_request(make_tl_object(message.chat_id_, vector{message.id_}, true)); } } struct FileGeneration { int64 id = 0; string destination; string source; int32 part_size = 0; int32 local_size = 0; int32 size = 0; }; vector pending_file_generations; void on_file_generation_start(const td_api::updateFileGenerationStart &update) { FileGeneration file_generation; file_generation.id = update.generation_id_; file_generation.destination = update.destination_path_; if (update.conversion_ == "#url#") { // TODO: actually download file_generation.source = "test.jpg"; file_generation.part_size = 1000000; } else { file_generation.source = update.original_path_; file_generation.part_size = to_integer(update.conversion_); } auto r_stat = stat(file_generation.source); if (r_stat.is_ok()) { auto size = r_stat.ok().size_; if (size <= 0 || size > 1500000000) { r_stat = Status::Error(400, size == 0 ? Slice("File is empty") : Slice("File is too big")); } } if (r_stat.is_ok()) { file_generation.size = narrow_cast(r_stat.ok().size_); if (file_generation.part_size <= 0) { file_generation.part_size = file_generation.size; } pending_file_generations.push_back(std::move(file_generation)); timeout_expired(); } else { send_request(make_tl_object( update.generation_id_, td_api::make_object(400, r_stat.error().message().str()))); } } int64 as_chat_id(Slice str) const { str = trim(str); if (str[0] == '@') { auto it = username_to_user_id_.find(to_lower(str.substr(1))); if (it != username_to_user_id_.end()) { return it->second; } auto it2 = username_to_supergroup_id.find(to_lower(str.substr(1))); if (it2 != username_to_supergroup_id.end()) { auto supergroup_id = it2->second; return static_cast(-1000'000'000'000ll) - supergroup_id; } LOG(ERROR) << "Can't resolve " << str; return 0; } if (str == "me") { return my_id_; } return to_integer(str); } vector as_chat_ids(Slice chat_ids, char delimiter = ' ') const { return transform(full_split(chat_ids, delimiter), [this](Slice str) { return as_chat_id(str); }); } static int64 as_message_id(Slice str) { str = trim(str); if (!str.empty() && str.back() == 's') { return to_integer(str) << 20; } return to_integer(str); } static vector as_message_ids(Slice message_ids_string, char delimiter = ' ') { return transform(full_split(message_ids_string, delimiter), as_message_id); } int32 as_user_id(Slice str) const { str = trim(str); if (str[0] == '@') { auto it = username_to_user_id_.find(to_lower(str.substr(1))); if (it != username_to_user_id_.end()) { return it->second; } LOG(ERROR) << "Can't find user " << str; return 0; } if (str == "me") { return my_id_; } return to_integer(str); } vector as_user_ids(Slice user_ids, char delimiter = ' ') const { return transform(full_split(user_ids, delimiter), [this](Slice str) { return as_user_id(str); }); } static int32 as_file_id(string str) { return to_integer(trim(std::move(str))); } static int32 as_call_id(string str) { return to_integer(trim(std::move(str))); } static td_api::object_ptr as_input_file_id(string str) { return make_tl_object(as_file_id(str)); } static tl_object_ptr as_local_file(string path) { return make_tl_object(trim(std::move(path))); } static tl_object_ptr as_generated_file(string original_path, string conversion, int32 expected_size = 0) { return make_tl_object(trim(original_path), trim(conversion), expected_size); } static tl_object_ptr as_location(string latitude, string longitude) { return make_tl_object(to_double(latitude), to_double(longitude)); } static bool as_bool(string str) { str = to_lower(str); return str == "true" || str == "1"; } template static vector to_integers(Slice ids_string, char delimiter = ' ') { return transform(full_split(ids_string, delimiter), to_integer); } void on_result(uint64 id, tl_object_ptr result) { if (id > 0 && GET_VERBOSITY_LEVEL() < VERBOSITY_NAME(td_requests)) { LOG(ERROR) << "on_result [id=" << id << "] " << to_string(result); } auto as_json_str = json_encode(ToJson(result)); // LOG(INFO) << "on_result [id=" << id << "] " << as_json_str; auto copy_as_json_str = as_json_str; auto as_json_value = json_decode(copy_as_json_str).move_as_ok(); td_api::object_ptr object; from_json(object, as_json_value).ensure(); CHECK(object != nullptr); auto as_json_str2 = json_encode(ToJson(object)); CHECK(as_json_str == as_json_str2) << "\n" << tag("a", as_json_str) << "\n" << tag("b", as_json_str2); // LOG(INFO) << "on_result [id=" << id << "] " << as_json_str; int32 result_id = result == nullptr ? 0 : result->get_id(); [&] { if (id != 0) { auto it = query_id_to_send_message_info_.find(id); if (it == query_id_to_send_message_info_.end()) { return; } auto info = it->second; query_id_to_send_message_info_.erase(id); if (result_id == td_api::message::ID) { auto *message = static_cast(result.get()); message_id_to_send_message_info_[message->id_] = info; } } }(); [&] { if (result_id == td_api::updateMessageSendAcknowledged::ID) { auto *message = static_cast(result.get()); auto it = message_id_to_send_message_info_.find(message->message_id_); if (it == message_id_to_send_message_info_.end()) { return; } auto &info = it->second; info.quick_ack_time = Time::now(); } }(); [&] { if (result_id == td_api::updateMessageSendSucceeded::ID) { auto *message = static_cast(result.get()); auto it = message_id_to_send_message_info_.find(message->old_message_id_); if (it == message_id_to_send_message_info_.end()) { return; } auto info = it->second; message_id_to_send_message_info_.erase(it); info.ack_time = Time::now(); LOG(INFO) << info; } }(); switch (result_id) { case td_api::updateUser::ID: register_user(*static_cast(result.get())->user_); break; case td_api::updateSupergroup::ID: register_supergroup(*static_cast(result.get())->supergroup_); break; case td_api::users::ID: update_users(*static_cast(result.get())); break; case td_api::updateOption::ID: update_option(*static_cast(result.get())); break; case td_api::message::ID: on_get_message(*static_cast(result.get())); break; case td_api::messages::ID: on_get_messages(*static_cast(result.get())); break; case td_api::updateFileGenerationStart::ID: on_file_generation_start(*static_cast(result.get())); break; case td_api::updateChatLastMessage::ID: { auto message = static_cast(result.get())->last_message_.get(); if (message != nullptr && message->content_->get_id() == td_api::messageText::ID) { // auto text = static_cast(message->content_.get())->text_->text_; } break; } } } void on_error(uint64 id, tl_object_ptr error) { auto current_verbosity_level = GET_VERBOSITY_LEVEL(); SET_VERBOSITY_LEVEL(VERBOSITY_NAME(INFO)); LOG(INFO) << "on_error [id=" << id << "] " << to_string(error); SET_VERBOSITY_LEVEL(current_verbosity_level); } void on_closed() { LOG(INFO) << "on_closed"; ready_to_stop_ = true; if (close_flag_) { yield(); return; } } void quit() { if (close_flag_) { return; } LOG(WARNING) << "QUIT"; close_flag_ = true; dump_memory_usage(); td_.reset(); #if TD_WINDOWS stdin_reader_.reset(); #else is_stdin_reader_stopped_ = true; #endif yield(); } #ifdef USE_READLINE Fd stdin_; #else using StreamConnection = BufferedFd; StreamConnection stdin_; #endif static CliClient *instance_; #ifdef USE_READLINE /* Callback function called for each line when accept-line executed, EOF * seen, or EOF character read. This sets a flag and returns; it could * also call exit. */ static void cb_linehandler(char *line) { /* Can use ^D (stty eof) to exit. */ if (line == nullptr) { LOG(FATAL) << "closed"; return; } if (*line) { add_history(line); } instance_->add_cmd(line); rl_free(line); } #endif unique_ptr make_td_callback() { class TdCallbackImpl : public TdCallback { public: explicit TdCallbackImpl(CliClient *client) : client_(client) { } void on_result(uint64 id, tl_object_ptr result) override { client_->on_result(id, std::move(result)); } void on_error(uint64 id, tl_object_ptr error) override { client_->on_error(id, std::move(error)); } void on_closed() override { client_->on_closed(); } private: CliClient *client_; }; return make_unique(this); } void init_td() { close_flag_ = false; ready_to_stop_ = false; td_ = create_actor("ClientActor1", make_td_callback()); td_ = create_actor("ClientActor2", make_td_callback()); ready_to_stop_ = false; auto bad_parameters = td_api::make_object(); bad_parameters->database_directory_ = "/.."; bad_parameters->api_id_ = api_id_; bad_parameters->api_hash_ = api_hash_; send_request(td_api::make_object(std::move(bad_parameters))); auto parameters = td_api::make_object(); parameters->use_test_dc_ = use_test_dc_; parameters->use_message_database_ = true; parameters->use_secret_chats_ = true; parameters->api_id_ = api_id_; parameters->api_hash_ = api_hash_; parameters->system_language_code_ = "en"; parameters->device_model_ = "Desktop"; parameters->system_version_ = "Unknown"; parameters->application_version_ = "tg_cli"; send_request(td_api::make_object(std::move(parameters))); send_request(td_api::make_object()); for (int i = 0; i < 4; i++) { send_closure_later(td_, &ClientActor::request, std::numeric_limits::max(), td_api::make_object(0.001 + 1000 * (i / 2))); } } void init() { instance_ = this; init_td(); #if TD_WINDOWS auto stdin_id = Scheduler::instance()->sched_count() - 1; class StdinReader : public Actor { public: explicit StdinReader(ActorShared parent) : parent_(std::move(parent)) { } void start_up() override { stdin_ = &Fd::Stdin(); set_timeout_in(0.001); } void timeout_expired() override { std::array buf; auto t_res = stdin_->read(MutableSlice(buf.data(), buf.size())); if (t_res.is_error()) { LOG(FATAL) << "Can't read from stdin"; } auto res = t_res.ok(); VLOG(fd) << res << " " << string(buf.data(), res); data_.append(string(buf.data(), res)); process(); set_timeout_in(0.05); } private: Fd *stdin_ = nullptr; string data_; ActorShared parent_; void process() { while (true) { auto pos = data_.find('\n'); if (pos == string::npos) { break; } auto cmd = string(data_.data(), pos); while (!cmd.empty() && cmd.back() == '\r') { cmd.pop_back(); } send_closure(parent_, &CliClient::on_cmd, cmd); data_ = data_.substr(pos + 1); } } }; stdin_reader_ = create_actor_on_scheduler("stdin_reader", stdin_id, actor_shared(this, 1)); #else Fd::Stdin().set_is_blocking(false).ensure(); #ifdef USE_READLINE deactivate_readline(); rl_callback_handler_install(prompt, cb_linehandler); rl_attempted_completion_function = tg_cli_completion; reactivate_readline(); stdin_ = Fd::Stdin().clone(); #else stdin_ = StreamConnection(Fd::Stdin().clone()); #endif stdin_.get_fd().set_observer(this); subscribe(stdin_, Fd::Read); #endif if (get_chat_list_) { send_request(make_tl_object(std::numeric_limits::max(), 0, 100)); } if (disable_network_) { send_request(make_tl_object(make_tl_object())); } } #ifndef USE_READLINE size_t buffer_pos_ = 0; Result process_stdin(ChainBufferReader *buffer) { auto found = find_boundary(buffer->clone(), "\n", buffer_pos_); if (!found) { return Status::Error("End of line not found"); } auto data = buffer->cut_head(buffer_pos_).move_as_buffer_slice(); if (!data.empty() && data[data.size() - 1] == '\r') { data.truncate(data.size() - 1); } buffer->cut_head(1); buffer_pos_ = 0; return std::move(data); } #endif static tl_object_ptr as_caption(string caption, vector> entities = {}) { if (entities.empty()) { auto parsed_caption = execute(make_tl_object(caption, make_tl_object())); if (parsed_caption->get_id() == td_api::formattedText::ID) { return td_api::move_object_as(parsed_caption); } } return make_tl_object(caption, std::move(entities)); } tl_object_ptr get_notification_settings_scope(Slice scope) const { if (scope == "users" || scope == "privateChats") { return make_tl_object(); } else if (scope == "chats" || scope == "groups" || scope == "groupChats") { return make_tl_object(); } else if (scope == "all" || scope == "dialogs") { return make_tl_object(); } else { return make_tl_object(as_chat_id(scope)); } } static tl_object_ptr get_user_privacy_setting(MutableSlice setting) { setting = trim(setting); to_lower_inplace(setting); if (setting == "invite") { return make_tl_object(); } if (setting == "status") { return make_tl_object(); } if (setting == "call") { return make_tl_object(); } return nullptr; } static tl_object_ptr get_search_messages_filter(MutableSlice filter) { filter = trim(filter); to_lower_inplace(filter); if (filter == "an" || filter == "animation") { return make_tl_object(); } if (filter == "au" || filter == "audio") { return make_tl_object(); } if (filter == "d" || filter == "document") { return make_tl_object(); } if (filter == "p" || filter == "photo") { return make_tl_object(); } if (filter == "vi" || filter == "video") { return make_tl_object(); } if (filter == "vo" || filter == "voice") { return make_tl_object(); } if (filter == "pvo") { return make_tl_object(); } if (filter == "u" || filter == "url") { return make_tl_object(); } if (filter == "cp" || filter == "chatphoto") { return make_tl_object(); } if (filter == "vc" || filter == "call") { return make_tl_object(); } if (filter == "mvc" || filter == "missedcall") { return make_tl_object(); } if (filter == "vn" || filter == "videonote") { return make_tl_object(); } if (filter == "vvn" || filter == "voicevideonote") { return make_tl_object(); } if (filter == "m" || filter == "mention") { return make_tl_object(); } if (filter == "um" || filter == "umention") { return make_tl_object(); } if (!filter.empty()) { LOG(ERROR) << "Unsupported message filter " << filter; } return nullptr; } tl_object_ptr get_top_chat_category(MutableSlice category) { category = trim(category); to_lower_inplace(category); if (!category.empty() && category.back() == 's') { category.remove_suffix(1); } if (category == "bot") { return make_tl_object(); } else if (category == "group") { return make_tl_object(); } else if (category == "channel") { return make_tl_object(); } else if (category == "inline") { return make_tl_object(); } else if (category == "call") { return make_tl_object(); } else { return make_tl_object(); } } static tl_object_ptr get_chat_action(MutableSlice action) { action = trim(action); to_lower_inplace(action); if (action == "c" || action == "cancel") { return make_tl_object(); } if (action == "rvi" || action == "record_video") { return make_tl_object(); } if (action == "uvi" || action == "upload_video") { return make_tl_object(50); } if (action == "rvo" || action == "record_voice") { return make_tl_object(); } if (action == "uvo" || action == "upload_voice") { return make_tl_object(50); } if (action == "up" || action == "upload_photo") { return make_tl_object(50); } if (action == "ud" || action == "upload_document") { return make_tl_object(50); } if (action == "fl" || action == "find_location") { return make_tl_object(); } if (action == "cc" || action == "choose_contact") { return make_tl_object(); } if (action == "spg" || action == "start_play_game") { return make_tl_object(); } if (action == "rvn" || action == "record_video_note") { return make_tl_object(); } if (action == "uvn" || action == "upload_video_note") { return make_tl_object(50); } return make_tl_object(); } static tl_object_ptr get_network_type(MutableSlice type) { type = trim(type); to_lower_inplace(type); if (type == "none") { return make_tl_object(); } if (type == "mobile") { return make_tl_object(); } if (type == "roaming") { return make_tl_object(); } if (type == "wifi") { return make_tl_object(); } if (type == "other") { return make_tl_object(); } return nullptr; } static td_api::object_ptr execute(tl_object_ptr f) { LOG(INFO) << "Execute request: " << to_string(f); auto res = ClientActor::execute(std::move(f)); LOG(INFO) << "Execute response: " << to_string(res); return res; } uint64 send_request(tl_object_ptr f) { static uint64 query_num = 1; if (!td_.empty()) { auto id = query_num++; send_closure_later(td_, &ClientActor::request, id, std::move(f)); return id; } else { LOG(ERROR) << "Failed to send: " << to_string(f); return 0; } } void send_message(const string &chat_id, tl_object_ptr &&input_message_content, bool disable_notification = false, bool from_background = false, int64 reply_to_message_id = 0) { auto chat = as_chat_id(chat_id); auto id = send_request(make_tl_object( chat, reply_to_message_id, disable_notification, from_background, nullptr, std::move(input_message_content))); query_id_to_send_message_info_[id].start_time = Time::now(); } void on_cmd(string cmd) { // TODO: need to remove https://en.wikipedia.org/wiki/ANSI_escape_code from cmd cmd.erase(std::remove_if(cmd.begin(), cmd.end(), [](char c) { return 0 <= c && c < 32; }), cmd.end()); LOG(INFO) << "CMD:[" << cmd << "]"; string op; string args; std::tie(op, args) = split(cmd); const int32 OP_BLOCK_COUNT = 5; int32 op_not_found_count = 0; if (op == "gas") { send_request(make_tl_object()); } else if (op == "sap") { send_request(make_tl_object(args, false, false)); } else if (op == "rac") { send_request(make_tl_object()); } else if (op == "cdek" || op == "CheckDatabaseEncryptionKey") { send_request(make_tl_object(args)); } else if (op == "sdek" || op == "SetDatabaseEncryptionKey") { send_request(make_tl_object(args)); } else if (op == "cac") { string code; string first_name; string last_name; std::tie(code, args) = split(args); std::tie(first_name, last_name) = split(args); send_request(make_tl_object(code, first_name, last_name)); } else if (op == "cap") { send_request(make_tl_object(args)); } else if (op == "cab") { send_request(make_tl_object(args)); } else if (op == "rapr") { send_request(make_tl_object()); } else if (op == "rap") { send_request(make_tl_object(args)); } else if (op == "lo" || op == "LogOut" || op == "logout") { send_request(make_tl_object()); } else if (op == "ra" || op == "destroy") { send_request(make_tl_object()); } else if (op == "reset") { init_td(); } else if (op == "close_td") { send_request(make_tl_object()); } else if (op == "DeleteAccountYesIReallyWantToDeleteMyAccount") { send_request(make_tl_object(args)); } else if (op == "gps" || op == "GetPasswordState") { send_request(make_tl_object()); } else if (op == "spass" || op == "SetPassword") { string password; string new_password; string new_hint; string recovery_email_address; std::tie(password, args) = split(args); if (password == "#") { password = ""; } std::tie(new_password, args) = split(args); if (new_password == "#") { new_password = ""; } std::tie(new_hint, args) = split(args); if (new_hint == "#") { new_hint = ""; } recovery_email_address = args; if (recovery_email_address == "#") { recovery_email_address = ""; } send_request(make_tl_object(password, new_password, new_hint, true, recovery_email_address)); } else if (op == "srea" || op == "SetRecoveryEmailAddress") { string password; string recovery_email_address; std::tie(password, recovery_email_address) = split(args); send_request(make_tl_object(password, recovery_email_address)); } else if (op == "rpr" || op == "RequestPasswordRecovery") { send_request(make_tl_object()); } else if (op == "rp" || op == "RecoverPassword") { send_request(make_tl_object(args)); } else if (op == "grea" || op == "GetRecoveryEmailAddress") { send_request(make_tl_object(args)); } else if (op == "gtp" || op == "GetTemporaryPassword") { send_request(make_tl_object()); } else if (op == "ctp" || op == "CreateTemporaryPassword") { send_request(make_tl_object(args, 60 * 6)); } else if (op == "pdu" || op == "processDcUpdate") { string dc_id; string ip_port; std::tie(dc_id, ip_port) = split(args); send_request(make_tl_object(dc_id, ip_port)); } else if (op == "rda") { send_request(make_tl_object(make_tl_object(args, true), as_user_ids(""))); } else if (op == "rdb") { send_request(make_tl_object(make_tl_object(args), as_user_ids(""))); } else if (op == "rdt") { string token; string other_user_ids_str; std::tie(token, other_user_ids_str) = split(args); send_request(make_tl_object(make_tl_object(token), as_user_ids(other_user_ids_str))); } else if (op == "rdu") { string token; string other_user_ids_str; std::tie(token, other_user_ids_str) = split(args); send_request(make_tl_object(make_tl_object(token), as_user_ids(other_user_ids_str))); } else if (op == "rdw") { string endpoint; string key; string secret; string other_user_ids_str; std::tie(endpoint, args) = split(args); std::tie(key, args) = split(args); std::tie(secret, other_user_ids_str) = split(args); send_request(make_tl_object( make_tl_object(endpoint, key, secret), as_user_ids(other_user_ids_str))); } else if (op == "gpf") { string chat_id; string message_id; std::tie(chat_id, message_id) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_message_id(message_id))); } else if (op == "voi") { string chat_id; string message_id; string allow_save; std::tie(chat_id, args) = split(args); std::tie(message_id, allow_save) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_message_id(message_id), nullptr, as_bool(allow_save))); } else if (op == "spfs") { string chat_id; string message_id; string order_info_id; string shipping_option_id; string saved_credentials_id; std::tie(chat_id, args) = split(args); std::tie(message_id, args) = split(args); std::tie(order_info_id, args) = split(args); std::tie(shipping_option_id, saved_credentials_id) = split(args); send_request(make_tl_object( as_chat_id(chat_id), as_message_id(message_id), order_info_id, shipping_option_id, make_tl_object(saved_credentials_id))); } else if (op == "spfn") { string chat_id; string message_id; string order_info_id; string shipping_option_id; string data; std::tie(chat_id, args) = split(args); std::tie(message_id, args) = split(args); std::tie(order_info_id, args) = split(args); std::tie(shipping_option_id, data) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_message_id(message_id), order_info_id, shipping_option_id, make_tl_object(data, true))); } else if (op == "gpre") { string chat_id; string message_id; std::tie(chat_id, message_id) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_message_id(message_id))); } else if (op == "gsoi") { send_request(make_tl_object()); } else if (op == "dsoi") { send_request(make_tl_object()); } else if (op == "dsc") { send_request(make_tl_object()); } else if (op == "gpr") { send_request(make_tl_object(get_user_privacy_setting(args))); } else if (op == "spr") { string setting; string allow; std::tie(setting, allow) = split(args); std::vector> rules; if (as_bool(allow)) { rules.push_back(make_tl_object()); } else { rules.push_back(make_tl_object()); } send_request(make_tl_object( get_user_privacy_setting(setting), make_tl_object(std::move(rules)))); } else if (op == "cp" || op == "ChangePhone") { send_request(make_tl_object(args, false, false)); } else if (op == "ccpc" || op == "CheckChangePhoneCode") { send_request(make_tl_object(args)); } else if (op == "rcpc" || op == "ResendChangePhoneCode") { send_request(make_tl_object()); } else if (op == "GetContacts") { auto limit = to_integer(args); if (limit == 0) { limit = 10000; } send_request(make_tl_object("", limit)); } else if (op == "ImportContacts") { vector contacts_str = full_split(args, ';'); vector> contacts; for (auto c : contacts_str) { string phone_number; string first_name; string last_name; std::tie(phone_number, c) = split(c, ','); std::tie(first_name, last_name) = split(c, ','); contacts.push_back(make_tl_object(phone_number, first_name, last_name, 0)); } send_request(make_tl_object(std::move(contacts))); } else if (op == "RemoveContacts") { send_request(make_tl_object(as_user_ids(args))); } else if (op == "gicc") { send_request(make_tl_object()); } else if (op == "cic") { vector contacts_str = full_split(args, ';'); vector> contacts; for (auto c : contacts_str) { string phone_number; string first_name; string last_name; std::tie(phone_number, c) = split(c, ','); std::tie(first_name, last_name) = split(c, ','); contacts.push_back(make_tl_object(phone_number, first_name, last_name, 0)); } send_request(make_tl_object(std::move(contacts))); } else if (op == "ClearImportedContacts") { send_request(make_tl_object()); } else { op_not_found_count++; } if (op == "gc" || op == "GetChats") { string offset_order_string; string offset_chat_id; string limit; std::tie(limit, args) = split(args); std::tie(offset_order_string, offset_chat_id) = split(args); if (limit.empty()) { limit = "10000"; } int64 offset_order; if (offset_order_string.empty()) { offset_order = std::numeric_limits::max(); } else { offset_order = to_integer(offset_order_string); } send_request( make_tl_object(offset_order, as_chat_id(offset_chat_id), to_integer(limit))); } else if (op == "gcc" || op == "GetCommonChats") { string user_id; string offset_chat_id; string limit; std::tie(user_id, args) = split(args); std::tie(offset_chat_id, limit) = split(args); if (limit.empty()) { limit = "100"; } send_request(make_tl_object(as_user_id(user_id), as_chat_id(offset_chat_id), to_integer(limit))); } else if (op == "gh" || op == "GetHistory" || op == "ghl") { string chat_id; string from_message_id; string offset; string limit; std::tie(chat_id, args) = split(args); std::tie(from_message_id, args) = split(args); if (from_message_id.empty()) { from_message_id = "0"; } std::tie(offset, args) = split(args); if (offset.empty()) { offset = "0"; } std::tie(limit, args) = split(args); if (limit.empty()) { limit = "10"; } if (!args.empty()) { LOG(ERROR) << "Wrong parameters to function getChatHistory specified"; } else { send_request(make_tl_object(as_chat_id(chat_id), as_message_id(from_message_id), to_integer(offset), to_integer(limit), op == "ghl")); } } else if (op == "ghf") { get_history_chat_id = as_chat_id(args); send_request(make_tl_object(get_history_chat_id, std::numeric_limits::max(), 0, 100, false)); } else if (op == "spvf") { search_chat_id = as_chat_id(args); send_request(make_tl_object( search_chat_id, "", 0, 0, 0, 100, make_tl_object())); } else if (op == "Search") { string from_date; string limit; string query; std::tie(query, args) = split(args); std::tie(limit, from_date) = split(args); if (from_date.empty()) { from_date = "0"; } send_request(make_tl_object(query, to_integer(from_date), 2147482647, 0, to_integer(limit))); } else if (op == "SCM") { string chat_id; string limit; string query; std::tie(chat_id, args) = split(args); std::tie(limit, query) = split(args); if (limit.empty()) { limit = "10"; } send_request(make_tl_object(as_chat_id(chat_id), query, 0, 0, 0, to_integer(limit), nullptr)); } else if (op == "SMME") { string chat_id; string limit; std::tie(chat_id, limit) = split(args); if (limit.empty()) { limit = "10"; } send_request(make_tl_object(as_chat_id(chat_id), "", my_id_, 0, 0, to_integer(limit), nullptr)); } else if (op == "SM") { string chat_id; string offset_message_id; string offset; string limit; string filter; std::tie(chat_id, args) = split(args); std::tie(filter, args) = split(args); std::tie(limit, args) = split(args); std::tie(offset_message_id, offset) = split(args); if (limit.empty()) { limit = "10"; } if (offset_message_id.empty()) { offset_message_id = "0"; } if (offset.empty()) { offset = "0"; } send_request(make_tl_object( as_chat_id(chat_id), "", 0, as_message_id(offset_message_id), to_integer(offset), to_integer(limit), get_search_messages_filter(filter))); } else if (op == "SC") { string limit; string offset_message_id; string only_missed; std::tie(limit, args) = split(args); std::tie(offset_message_id, only_missed) = split(args); if (limit.empty()) { limit = "10"; } if (offset_message_id.empty()) { offset_message_id = "0"; } send_request(make_tl_object(as_message_id(offset_message_id), to_integer(limit), as_bool(only_missed))); } else if (op == "SCRLM") { string chat_id; string limit; std::tie(chat_id, limit) = split(args); if (limit.empty()) { limit = "10"; } send_request( make_tl_object(as_chat_id(chat_id), to_integer(limit))); } else if (op == "SearchAudio") { string chat_id; string offset_message_id; string limit; string query; std::tie(chat_id, args) = split(args); std::tie(offset_message_id, args) = split(args); if (offset_message_id.empty()) { offset_message_id = "0"; } std::tie(limit, query) = split(args); if (limit.empty()) { limit = "10"; } send_request(make_tl_object( as_chat_id(chat_id), query, 0, as_message_id(offset_message_id), 0, to_integer(limit), make_tl_object())); } else if (op == "SearchDocument") { string chat_id; string offset_message_id; string limit; string query; std::tie(chat_id, args) = split(args); std::tie(offset_message_id, args) = split(args); if (offset_message_id.empty()) { offset_message_id = "0"; } std::tie(limit, query) = split(args); if (limit.empty()) { limit = "10"; } send_request(make_tl_object( as_chat_id(chat_id), query, 0, to_integer(offset_message_id), 0, to_integer(limit), make_tl_object())); } else if (op == "SearchPhoto") { string chat_id; string offset_message_id; string limit; string query; std::tie(chat_id, args) = split(args); std::tie(offset_message_id, args) = split(args); if (offset_message_id.empty()) { offset_message_id = "2000000000000000000"; } std::tie(limit, query) = split(args); if (limit.empty()) { limit = "10"; } send_request(make_tl_object( as_chat_id(chat_id), query, 0, as_message_id(offset_message_id), 0, to_integer(limit), make_tl_object())); } else if (op == "SearchChatPhoto") { string chat_id; string offset_message_id; string limit; string query; std::tie(chat_id, args) = split(args); std::tie(offset_message_id, args) = split(args); if (offset_message_id.empty()) { offset_message_id = "2000000000000000000"; } std::tie(limit, query) = split(args); if (limit.empty()) { limit = "10"; } send_request(make_tl_object( as_chat_id(chat_id), query, 0, as_message_id(offset_message_id), 0, to_integer(limit), make_tl_object())); } else if (op == "gup" || op == "GetUserPhotos") { string user_id; string offset; string limit; std::tie(user_id, args) = split(args); std::tie(offset, args) = split(args); if (offset.empty()) { offset = "0"; } std::tie(limit, args) = split(args); if (limit.empty()) { limit = "10"; } if (!args.empty()) { LOG(ERROR) << "Wrong parameters to function getUserProfilePhotos specified"; } else { send_request(make_tl_object(as_user_id(user_id), to_integer(offset), to_integer(limit))); } } else if (op == "dcrm") { string chat_id; string message_id; std::tie(chat_id, message_id) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_message_id(message_id))); } else if (op == "go") { send_request(make_tl_object(args)); } else if (op == "sob") { string name; string value; std::tie(name, value) = split(args); send_request(make_tl_object(name, make_tl_object(as_bool(value)))); } else if (op == "soe") { send_request(make_tl_object(args, make_tl_object())); } else if (op == "soi") { string name; string value; std::tie(name, value) = split(args); int32 value_int = to_integer(value); send_request(make_tl_object(name, make_tl_object(value_int))); } else if (op == "sos") { string name; string value; std::tie(name, value) = split(args); send_request(make_tl_object(name, make_tl_object(value))); } else if (op == "me") { send_request(make_tl_object()); } else if (op == "sattl") { send_request(make_tl_object(make_tl_object(to_integer(args)))); } else if (op == "gattl") { send_request(make_tl_object()); } else if (op == "GetActiveSessions") { send_request(make_tl_object()); } else if (op == "TerminateSession") { send_request(make_tl_object(to_integer(args))); } else if (op == "TerminateAllOtherSessions") { send_request(make_tl_object()); } else if (op == "gcw") { send_request(make_tl_object()); } else if (op == "dw") { send_request(make_tl_object(to_integer(args))); } else if (op == "daw") { send_request(make_tl_object()); } else if (op == "gw") { send_request(make_tl_object()); } else if (op == "git") { send_request(make_tl_object()); } else if (op == "gtos") { send_request(make_tl_object()); } else if (op == "tme") { send_request(make_tl_object(args)); } else if (op == "bu") { send_request(make_tl_object(as_user_id(args))); } else if (op == "ubu") { send_request(make_tl_object(as_user_id(args))); } else if (op == "gbu") { string offset; string limit; std::tie(offset, limit) = split(args); if (offset.empty()) { offset = "0"; } if (limit.empty()) { limit = "10"; } send_request(make_tl_object(to_integer(offset), to_integer(limit))); } else if (op == "gu") { send_request(make_tl_object(as_user_id(args))); } else if (op == "gsu") { send_request(make_tl_object()); } else if (op == "gs") { string limit; string emoji; std::tie(limit, emoji) = split(args); send_request(make_tl_object(emoji, to_integer(limit))); } else if (op == "gss") { send_request(make_tl_object(to_integer(args))); } else if (op == "giss") { send_request(make_tl_object(as_bool(args))); } else if (op == "gass") { string is_masks; string offset_sticker_set_id; string limit; std::tie(is_masks, args) = split(args); std::tie(offset_sticker_set_id, limit) = split(args); send_request(make_tl_object( as_bool(is_masks), to_integer(offset_sticker_set_id), to_integer(limit))); } else if (op == "gtss") { send_request(make_tl_object()); } else if (op == "gatss") { send_request(make_tl_object(to_integer(args))); } else if (op == "storage") { send_request(make_tl_object(to_integer(args))); } else if (op == "storage_fast") { send_request(make_tl_object()); } else if (op == "optimize_storage") { string chat_ids; string exclude_chat_ids; string chat_ids_limit; std::tie(chat_ids, args) = split(args); std::tie(exclude_chat_ids, chat_ids_limit) = split(args); send_request(make_tl_object( 10000000, -1, -1, 0, std::vector>(), as_chat_ids(chat_ids, ','), as_chat_ids(exclude_chat_ids, ','), to_integer(chat_ids_limit))); } else if (op == "clean_storage_default") { send_request(make_tl_object()); } else if (op == "clean_storage") { std::vector> types; types.push_back(make_tl_object()); types.push_back(make_tl_object()); types.push_back(make_tl_object()); types.push_back(make_tl_object()); types.push_back(make_tl_object()); types.push_back(make_tl_object()); types.push_back(make_tl_object()); types.push_back(make_tl_object()); types.push_back(make_tl_object()); types.push_back(make_tl_object()); types.push_back(make_tl_object()); types.push_back(make_tl_object()); send_request(make_tl_object(0, -1, -1, 0, std::move(types), as_chat_ids(args, ','), as_chat_ids(""), 20)); } else if (op == "network") { send_request(make_tl_object()); } else if (op == "current_network") { send_request(make_tl_object(true)); } else if (op == "reset_network") { send_request(make_tl_object()); } else if (op == "snt") { send_request(make_tl_object(get_network_type(args))); } else if (op == "ansc") { string sent_bytes; string received_bytes; string duration; string network_type; std::tie(sent_bytes, args) = split(args); std::tie(received_bytes, args) = split(args); std::tie(duration, network_type) = split(args); send_request(make_tl_object(make_tl_object( get_network_type(network_type), to_integer(sent_bytes), to_integer(received_bytes), to_double(duration)))); } else if (op == "ans") { string sent_bytes; string received_bytes; string network_type; std::tie(sent_bytes, args) = split(args); std::tie(received_bytes, network_type) = split(args); send_request(make_tl_object(make_tl_object( make_tl_object(), get_network_type(network_type), to_integer(sent_bytes), to_integer(received_bytes)))); } else if (op == "top_chats") { send_request(make_tl_object(get_top_chat_category(args), 50)); } else if (op == "rtc") { string chat_id; string category; std::tie(chat_id, category) = split(args); send_request(make_tl_object(get_top_chat_category(category), as_chat_id(chat_id))); } else if (op == "sss") { send_request(make_tl_object(args)); } else if (op == "css") { string set_id; string is_installed; string is_archived; std::tie(set_id, args) = split(args); std::tie(is_installed, is_archived) = split(args); send_request(make_tl_object(to_integer(set_id), as_bool(is_installed), as_bool(is_archived))); } else if (op == "vtss") { send_request(make_tl_object(to_integers(args))); } else if (op == "riss") { string is_masks; string new_order; std::tie(is_masks, new_order) = split(args); send_request( make_tl_object(as_bool(is_masks), to_integers(new_order))); } else if (op == "grs") { send_request(make_tl_object(as_bool(args))); } else if (op == "ars") { string is_attached; string sticker_id; std::tie(is_attached, sticker_id) = split(args); send_request(make_tl_object(as_bool(is_attached), as_input_file_id(sticker_id))); } else if (op == "rrs") { string is_attached; string sticker_id; std::tie(is_attached, sticker_id) = split(args); send_request(make_tl_object(as_bool(is_attached), as_input_file_id(sticker_id))); } else if (op == "gfs") { send_request(make_tl_object()); } else if (op == "afs") { send_request(make_tl_object(as_input_file_id(args))); } else if (op == "rfs") { send_request(make_tl_object(as_input_file_id(args))); } else if (op == "crs") { send_request(make_tl_object(as_bool(args))); } else if (op == "gse") { send_request(make_tl_object(as_input_file_id(args))); } else { op_not_found_count++; } if (op == "gsan") { send_request(make_tl_object()); } else if (op == "asan") { send_request(make_tl_object(as_input_file_id(args))); } else if (op == "rsan") { send_request(make_tl_object(as_input_file_id(args))); } else if (op == "guf") { send_request(make_tl_object(as_user_id(args))); } else if (op == "gbg") { send_request(make_tl_object(to_integer(args))); } else if (op == "gbgf") { send_request(make_tl_object(to_integer(args))); } else if (op == "gsg" || op == "gch") { send_request(make_tl_object(to_integer(args))); } else if (op == "gsgf" || op == "gchf") { send_request(make_tl_object(to_integer(args))); } else if (op == "gsc") { send_request(make_tl_object(to_integer(args))); } else if (op == "scm") { string chat_id; string limit; string query; std::tie(chat_id, args) = split(args); std::tie(limit, query) = split(args); send_request(make_tl_object(as_chat_id(chat_id), query, to_integer(limit))); } else if (op == "gcm") { string chat_id; string user_id; std::tie(chat_id, user_id) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_user_id(user_id))); } else if (op == "GetSupergroupAdministrators") { string supergroup_id; string offset; string limit; std::tie(supergroup_id, args) = split(args); std::tie(offset, limit) = split(args); if (offset.empty()) { offset = "0"; } if (limit.empty()) { limit = "10"; } send_request(make_tl_object( to_integer(supergroup_id), make_tl_object(), to_integer(offset), to_integer(limit))); } else if (op == "GetChatAdministrators") { string chat_id = args; send_request(make_tl_object(as_chat_id(chat_id))); } else if (op == "GetSupergroupBanned") { string supergroup_id; string query; string offset; string limit; std::tie(supergroup_id, args) = split(args); std::tie(query, args) = split(args); std::tie(offset, limit) = split(args); if (offset.empty()) { offset = "0"; } if (limit.empty()) { limit = "10"; } send_request(make_tl_object( to_integer(supergroup_id), make_tl_object(query), to_integer(offset), to_integer(limit))); } else if (op == "GetSupergroupBots") { string supergroup_id; string offset; string limit; std::tie(supergroup_id, args) = split(args); std::tie(offset, limit) = split(args); if (offset.empty()) { offset = "0"; } if (limit.empty()) { limit = "10"; } send_request(make_tl_object(to_integer(supergroup_id), make_tl_object(), to_integer(offset), to_integer(limit))); } else if (op == "GetSupergroupMembers") { string supergroup_id; string offset; string limit; std::tie(supergroup_id, args) = split(args); std::tie(offset, limit) = split(args); if (offset.empty()) { offset = "0"; } if (limit.empty()) { limit = "10"; } send_request(make_tl_object(to_integer(supergroup_id), make_tl_object(), to_integer(offset), to_integer(limit))); } else if (op == "SearchSupergroupMembers") { string supergroup_id; string query; string offset; string limit; std::tie(supergroup_id, args) = split(args); std::tie(query, args) = split(args); std::tie(offset, limit) = split(args); if (offset.empty()) { offset = "0"; } if (limit.empty()) { limit = "10"; } send_request(make_tl_object( to_integer(supergroup_id), make_tl_object(query), to_integer(offset), to_integer(limit))); } else if (op == "GetSupergroupRestricted") { string supergroup_id; string query; string offset; string limit; std::tie(supergroup_id, args) = split(args); std::tie(query, args) = split(args); std::tie(offset, limit) = split(args); if (offset.empty()) { offset = "0"; } if (limit.empty()) { limit = "10"; } send_request(make_tl_object( to_integer(supergroup_id), make_tl_object(query), to_integer(offset), to_integer(limit))); } else if (op == "gdialog" || op == "gd") { send_request(make_tl_object(as_chat_id(args))); } else if (op == "open") { send_request(make_tl_object(as_chat_id(args))); } else if (op == "close") { send_request(make_tl_object(as_chat_id(args))); } else if (op == "gm") { string chat_id; string message_id; std::tie(chat_id, message_id) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_message_id(message_id))); } else if (op == "grm") { string chat_id; string message_id; std::tie(chat_id, message_id) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_message_id(message_id))); } else if (op == "gcpm") { string chat_id = args; send_request(make_tl_object(as_chat_id(chat_id))); } else if (op == "gms") { string chat_id; string message_ids; std::tie(chat_id, message_ids) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_message_ids(message_ids))); } else if (op == "gpml") { string chat_id; string message_id; string for_album; std::tie(chat_id, args) = split(args); std::tie(message_id, for_album) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_message_id(message_id), as_bool(for_album))); } else if (op == "gcmbd") { string chat_id; string date; std::tie(chat_id, date) = split(args); send_request(make_tl_object(as_chat_id(chat_id), to_integer(date))); } else if (op == "gf" || op == "GetFile") { send_request(make_tl_object(as_file_id(args))); } else if (op == "grf") { send_request(make_tl_object(args, nullptr)); } else if (op == "df" || op == "DownloadFile") { string file_id; string priority; std::tie(file_id, priority) = split(args); if (priority.empty()) { priority = "1"; } send_request(make_tl_object(as_file_id(file_id), to_integer(priority))); } else if (op == "dff") { string file_id; string priority; std::tie(file_id, priority) = split(args); if (priority.empty()) { priority = "1"; } for (int i = 1; i <= as_file_id(file_id); i++) { send_request(make_tl_object(i, to_integer(priority))); } } else if (op == "cdf") { send_request(make_tl_object(as_file_id(args), true)); } else if (op == "uf") { string file_path; string priority; std::tie(file_path, priority) = split(args); if (priority.empty()) { priority = "1"; } send_request(make_tl_object(as_local_file(file_path), make_tl_object(), to_integer(priority))); } else if (op == "ufs") { string file_path; string priority; std::tie(file_path, priority) = split(args); if (priority.empty()) { priority = "1"; } send_request(make_tl_object( as_local_file(file_path), make_tl_object(), to_integer(priority))); } else if (op == "ufg") { string file_path; string conversion; std::tie(file_path, conversion) = split(args); send_request(make_tl_object(as_generated_file(file_path, conversion), make_tl_object(), 1)); } else if (op == "cuf") { send_request(make_tl_object(as_file_id(args))); } else if (op == "delf" || op == "DeleteFile") { string file_id = args; send_request(make_tl_object(as_file_id(file_id))); } else if (op == "dm") { string chat_id; string message_ids_string; string revoke; std::tie(chat_id, args) = split(args); std::tie(message_ids_string, revoke) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_message_ids(message_ids_string, ','), as_bool(revoke))); } else if (op == "fm" || op == "fmg") { string chat_id; string from_chat_id; string message_ids_string; std::tie(chat_id, args) = split(args); std::tie(from_chat_id, message_ids_string) = split(args); auto chat = as_chat_id(chat_id); send_request(make_tl_object( chat, as_chat_id(from_chat_id), as_message_ids(message_ids_string), false, false, op == "fmg")); } else if (op == "csc" || op == "CreateSecretChat") { send_request(make_tl_object(to_integer(args))); } else if (op == "cnsc" || op == "CreateNewSecretChat") { send_request(make_tl_object(as_user_id(args))); } else if (op == "scstn") { send_request(make_tl_object(as_chat_id(args))); } else if (op == "sscttl" || op == "setSecretChatTtl") { string chat_id; string ttl; std::tie(chat_id, ttl) = split(args); send_request(make_tl_object(as_chat_id(chat_id), to_integer(ttl))); } else if (op == "closeSC" || op == "cancelSC") { send_request(make_tl_object(to_integer(args))); } else if (op == "cc" || op == "CreateCall") { send_request(make_tl_object(as_user_id(args), make_tl_object(true, true, 65, 65))); } else if (op == "dc" || op == "DiscardCall") { string call_id; string is_disconnected; std::tie(call_id, is_disconnected) = split(args); send_request(make_tl_object(as_call_id(call_id), as_bool(is_disconnected), 0, 0)); } else if (op == "ac" || op == "AcceptCall") { send_request(make_tl_object(as_call_id(args), make_tl_object(true, true, 65, 65))); } else if (op == "scr" || op == "SendCallRating") { send_request(make_tl_object(as_call_id(args), 5, "Wow, such good call! (TDLib test)")); } else if (op == "scdi" || op == "SendCallDebugInformation") { send_request(make_tl_object(as_call_id(args), "{}")); } else if (op == "gcil") { send_request(make_tl_object(as_chat_id(args))); } else if (op == "ccil") { send_request(make_tl_object(args)); } else if (op == "jcbil") { send_request(make_tl_object(args)); } else if (op == "gte") { send_request(make_tl_object(args)); } else if (op == "gtes") { execute(make_tl_object(args)); } else if (op == "pte") { send_request(make_tl_object(args, make_tl_object())); } else if (op == "ptes") { execute(make_tl_object(args, make_tl_object())); } else if (op == "gfmt") { send_request(make_tl_object(trim(args))); } else if (op == "gfe") { send_request(make_tl_object(trim(args))); } else { op_not_found_count++; } if (op == "scdm") { string chat_id; string reply_to_message_id; string message; std::tie(chat_id, args) = split(args); std::tie(reply_to_message_id, message) = split(args); tl_object_ptr draft_message; if (!reply_to_message_id.empty() || !message.empty()) { vector> entities; entities.push_back(make_tl_object(0, 1, make_tl_object())); draft_message = make_tl_object( as_message_id(reply_to_message_id), make_tl_object( make_tl_object(message, std::move(entities)), true, false)); } send_request(make_tl_object(as_chat_id(chat_id), std::move(draft_message))); } else if (op == "tcip") { string chat_id; string is_pinned; std::tie(chat_id, is_pinned) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_bool(is_pinned))); } else if (op == "spchats") { vector chat_ids_str = full_split(args, ' '); vector chat_ids; for (auto &chat_id_str : chat_ids_str) { chat_ids.push_back(as_chat_id(chat_id_str)); } send_request(make_tl_object(std::move(chat_ids))); } else if (op == "sca") { string chat_id; string action; std::tie(chat_id, action) = split(args); send_request(make_tl_object(as_chat_id(chat_id), get_chat_action(action))); } else if (op == "smt" || op == "smtp" || op == "smtf" || op == "smtpf") { const string &chat_id = args; for (int i = 1; i <= 200; i++) { string message = PSTRING() << "#" << i; if (i == 6 || (op.back() == 'f' && i % 2 == 0)) { message = string(4097, 'a'); } if (op[3] == 'p') { send_message(chat_id, make_tl_object(as_local_file("rgb.jpg"), nullptr, Auto(), 0, 0, as_caption(message), 0)); } else { send_message(chat_id, make_tl_object( make_tl_object(message, vector>()), false, true)); } } } else if (op == "ssm") { string chat_id; string from_search_id; string limit; string filter; string query; std::tie(chat_id, args) = split(args); std::tie(from_search_id, args) = split(args); std::tie(limit, args) = split(args); std::tie(filter, query) = split(args); send_request( make_tl_object(as_chat_id(chat_id), query, to_integer(from_search_id), to_integer(limit), get_search_messages_filter(filter))); } else if (op == "sm" || op == "sms" || op == "smr" || op == "smf") { string chat_id; string reply_to_message_id; string message; std::tie(chat_id, message) = split(args); if (op == "smr") { std::tie(reply_to_message_id, message) = split(message); } if (op == "smf") { message = string(1000097, 'a'); } auto parsed_text = execute(make_tl_object(message, make_tl_object())); if (parsed_text->get_id() == td_api::error::ID) { parsed_text = make_tl_object(message, vector>()); } send_message( chat_id, make_tl_object(move_tl_object_as(parsed_text), false, true), op == "sms", false, as_message_id(reply_to_message_id)); } else if (op == "smap" || op == "smapr") { string chat_id; string reply_to_message_id; vector photos; std::tie(chat_id, args) = split(args); if (op == "smapr") { std::tie(reply_to_message_id, args) = split(args); } photos = full_split(args); send_request(make_tl_object( as_chat_id(chat_id), as_message_id(reply_to_message_id), false, false, transform(photos, [](const string &photo_path) { tl_object_ptr content = make_tl_object( as_local_file(photo_path), nullptr, Auto(), 0, 0, as_caption(""), 0); return content; }))); } else if (op == "em") { string chat_id; string message_id; string message; std::tie(chat_id, args) = split(args); std::tie(message_id, message) = split(args); send_request(make_tl_object( as_chat_id(chat_id), as_message_id(message_id), nullptr, make_tl_object( make_tl_object(message, vector>()), true, true))); } else if (op == "emll") { string chat_id; string message_id; string latitude; string longitude; std::tie(chat_id, args) = split(args); std::tie(message_id, args) = split(args); std::tie(latitude, longitude) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_message_id(message_id), nullptr, as_location(latitude, longitude))); } else if (op == "gallm") { send_request(make_tl_object()); } else if (op == "sbsm") { string bot_id; string chat_id; string parameter; std::tie(bot_id, args) = split(args); std::tie(chat_id, parameter) = split(args); send_request(make_tl_object(as_user_id(bot_id), as_chat_id(chat_id), parameter)); } else if (op == "giqr") { string bot_id; string query; std::tie(bot_id, query) = split(args); send_request(make_tl_object(as_user_id(bot_id), 0, nullptr, query, "")); } else if (op == "giqro") { string bot_id; string offset; string query; std::tie(bot_id, args) = split(args); std::tie(offset, query) = split(args); send_request(make_tl_object(as_user_id(bot_id), 0, nullptr, query, offset)); } else if (op == "giqrl") { string bot_id; string query; std::tie(bot_id, query) = split(args); send_request( make_tl_object(as_user_id(bot_id), 0, as_location("1.1", "2.2"), query, "")); } else if (op == "siqr") { string chat_id; string query_id; string result_id; std::tie(chat_id, args) = split(args); std::tie(query_id, result_id) = split(args); auto chat = as_chat_id(chat_id); send_request(make_tl_object(chat, 0, false, false, to_integer(query_id), result_id)); } else if (op == "gcqr") { string chat_id; string message_id; string data; std::tie(chat_id, args) = split(args); std::tie(message_id, data) = split(args); send_request(make_tl_object( as_chat_id(chat_id), as_message_id(message_id), make_tl_object(data))); } else if (op == "gcgqr") { string chat_id; string message_id; std::tie(chat_id, message_id) = split(args); send_request(make_tl_object( as_chat_id(chat_id), as_message_id(message_id), make_tl_object(""))); } else if (op == "san") { string chat_id; string animation_path; string width; string height; string caption; std::tie(chat_id, args) = split(args); std::tie(animation_path, args) = split(args); std::tie(width, args) = split(args); std::tie(height, caption) = split(args); send_message(chat_id, make_tl_object( as_local_file(animation_path), nullptr, 60, to_integer(width), to_integer(height), as_caption(caption))); } else if (op == "sang") { string chat_id; string animation_path; string animation_conversion; std::tie(chat_id, args) = split(args); std::tie(animation_path, animation_conversion) = split(args); send_message(chat_id, make_tl_object( as_generated_file(animation_path, animation_conversion), nullptr, 60, 0, 0, as_caption(""))); } else if (op == "sanid") { string chat_id; string file_id; std::tie(chat_id, file_id) = split(args); send_message(chat_id, make_tl_object(as_input_file_id(file_id), nullptr, 0, 0, 0, as_caption(""))); } else if (op == "sanurl") { string chat_id; string url; std::tie(chat_id, url) = split(args); send_message(chat_id, make_tl_object( td_api::make_object(url, "#url#", 0), nullptr, 0, 0, 0, as_caption(""))); } else if (op == "sanurl2") { string chat_id; string url; std::tie(chat_id, url) = split(args); send_message(chat_id, make_tl_object( td_api::make_object(url), nullptr, 0, 0, 0, as_caption(""))); } else if (op == "sau") { string chat_id; string audio_path; string duration; string title; string performer; std::tie(chat_id, args) = split(args); std::tie(audio_path, args) = split(args); std::tie(duration, args) = split(args); std::tie(title, performer) = split(args); send_message(chat_id, make_tl_object(as_local_file(audio_path), nullptr, to_integer(duration), title, performer, as_caption("audio caption"))); } else if (op == "svoice") { string chat_id; string voice_path; std::tie(chat_id, voice_path) = split(args); send_message(chat_id, make_tl_object(as_local_file(voice_path), 0, "abacaba", as_caption("voice caption"))); } else if (op == "SendContact" || op == "scontact") { string chat_id; string phone_number; string first_name; string last_name; string user_id; std::tie(chat_id, args) = split(args); std::tie(phone_number, args) = split(args); std::tie(first_name, args) = split(args); std::tie(last_name, user_id) = split(args); send_message(chat_id, make_tl_object(make_tl_object( phone_number, first_name, last_name, as_user_id(user_id)))); } else if (op == "sf") { string chat_id; string from_chat_id; string from_message_id; std::tie(chat_id, args) = split(args); std::tie(from_chat_id, from_message_id) = split(args); send_message(chat_id, make_tl_object(as_chat_id(from_chat_id), as_message_id(from_message_id), true)); } else if (op == "sd") { string chat_id; string document_path; std::tie(chat_id, document_path) = split(args); send_message(chat_id, make_tl_object( as_local_file(document_path), nullptr, Random::fast(0, 1) ? as_caption(u8"\u1680\u180Etest \u180E\n\u180E\n\u180E\n cap\ttion\u180E\u180E") : as_caption(u8"\u200C\u200D\u202E " " " " " " " " abacaba"))); } else if (op == "sdt") { string chat_id; string document_path; string thumbnail_path; std::tie(chat_id, args) = split(args); std::tie(document_path, thumbnail_path) = split(args); send_message(chat_id, make_tl_object( as_local_file(document_path), make_tl_object(as_local_file(thumbnail_path), 0, 0), as_caption("test caption"))); } else if (op == "sdg") { string chat_id; string document_path; string document_conversion; std::tie(chat_id, args) = split(args); std::tie(document_path, document_conversion) = split(args); send_message(chat_id, make_tl_object(as_generated_file(document_path, document_conversion), nullptr, as_caption("test caption"))); } else if (op == "sdtg") { string chat_id; string document_path; string thumbnail_path; string thumbnail_conversion; std::tie(chat_id, args) = split(args); std::tie(document_path, args) = split(args); std::tie(thumbnail_path, thumbnail_conversion) = split(args); send_message(chat_id, make_tl_object( as_local_file(document_path), make_tl_object( as_generated_file(thumbnail_path, thumbnail_conversion), 0, 0), as_caption("test caption"))); } else if (op == "sdgtg") { string chat_id; string document_path; string document_conversion; string thumbnail_path; string thumbnail_conversion; std::tie(chat_id, args) = split(args); std::tie(document_path, args) = split(args); std::tie(document_conversion, args) = split(args); std::tie(thumbnail_path, thumbnail_conversion) = split(args); send_message(chat_id, make_tl_object( as_generated_file(document_path, document_conversion), make_tl_object( as_generated_file(thumbnail_path, thumbnail_conversion), 0, 0), as_caption("test caption"))); } else if (op == "sdid") { string chat_id; string file_id; std::tie(chat_id, file_id) = split(args); send_message(chat_id, make_tl_object(as_input_file_id(file_id), nullptr, as_caption(""))); } else if (op == "sg") { string chat_id; string bot_user_id; string game_short_name; std::tie(chat_id, args) = split(args); std::tie(bot_user_id, game_short_name) = split(args); send_message(chat_id, make_tl_object(as_user_id(bot_user_id), game_short_name)); } else if (op == "sl") { string chat_id; std::tie(chat_id, args) = split(args); string latitude; string longitude; std::tie(latitude, longitude) = split(args); send_message(chat_id, make_tl_object(as_location(latitude, longitude), 0)); } else if (op == "sll") { string chat_id; string period; string latitude; string longitude; std::tie(chat_id, args) = split(args); std::tie(period, args) = split(args); std::tie(latitude, longitude) = split(args); send_message(chat_id, make_tl_object(as_location(latitude, longitude), to_integer(period))); } else if (op == "sp") { string chat_id; string photo_path; string sticker_file_ids_str; vector sticker_file_ids; std::tie(chat_id, args) = split(args); std::tie(sticker_file_ids_str, photo_path) = split(args); if (trim(photo_path).empty()) { photo_path = sticker_file_ids_str; } else { sticker_file_ids = to_integers(sticker_file_ids_str, ','); } send_message(chat_id, make_tl_object(as_local_file(photo_path), nullptr, std::move(sticker_file_ids), 0, 0, as_caption(""), 0)); } else if (op == "spttl") { string chat_id; string photo_path; std::tie(chat_id, photo_path) = split(args); send_message(chat_id, make_tl_object(as_local_file(photo_path), nullptr, Auto(), 0, 0, as_caption(""), 10)); } else if (op == "spg") { string chat_id; string photo_path; string conversion; std::tie(chat_id, args) = split(args); std::tie(photo_path, conversion) = split(args); send_message(chat_id, make_tl_object(as_generated_file(photo_path, conversion), nullptr, vector(), 0, 0, as_caption(""), 0)); } else if (op == "spt") { string chat_id; string photo_path; string thumbnail_path; std::tie(chat_id, args) = split(args); std::tie(photo_path, thumbnail_path) = split(args); send_message(chat_id, make_tl_object( as_local_file(photo_path), make_tl_object(as_local_file(thumbnail_path), 90, 89), vector(), 0, 0, as_caption(""), 0)); } else if (op == "sptg") { string chat_id; string photo_path; string thumbnail_path; string thumbnail_conversion; std::tie(chat_id, args) = split(args); std::tie(photo_path, args) = split(args); std::tie(thumbnail_path, thumbnail_conversion) = split(args); send_message(chat_id, make_tl_object( as_local_file(photo_path), make_tl_object( as_generated_file(thumbnail_path, thumbnail_conversion), 90, 89), vector(), 0, 0, as_caption(""), 0)); } else if (op == "spgtg") { string chat_id; string photo_path; string conversion; string thumbnail_path; string thumbnail_conversion; std::tie(chat_id, args) = split(args); std::tie(photo_path, args) = split(args); std::tie(conversion, args) = split(args); std::tie(thumbnail_path, thumbnail_conversion) = split(args); send_message(chat_id, make_tl_object( as_generated_file(photo_path, conversion), make_tl_object( as_generated_file(thumbnail_path, thumbnail_conversion), 90, 89), vector(), 0, 0, as_caption(""), 0)); } else if (op == "spid") { string chat_id; string file_id; std::tie(chat_id, file_id) = split(args); send_message(chat_id, make_tl_object(as_input_file_id(file_id), nullptr, vector(), 0, 0, as_caption(""), 0)); } else if (op == "ss") { string chat_id; string sticker_path; std::tie(chat_id, sticker_path) = split(args); send_message(chat_id, make_tl_object(as_local_file(sticker_path), nullptr, 0, 0)); } else if (op == "ssid") { string chat_id; string file_id; std::tie(chat_id, file_id) = split(args); send_message(chat_id, make_tl_object(as_input_file_id(file_id), nullptr, 0, 0)); } else if (op == "sv") { string chat_id; string video_path; string sticker_file_ids_str; vector sticker_file_ids; std::tie(chat_id, args) = split(args); std::tie(sticker_file_ids_str, video_path) = split(args); if (trim(video_path).empty()) { video_path = sticker_file_ids_str; } else { sticker_file_ids = to_integers(sticker_file_ids_str, ','); } send_message(chat_id, make_tl_object(as_local_file(video_path), nullptr, std::move(sticker_file_ids), 1, 2, 3, true, as_caption(""), 0)); } else if (op == "svn") { string chat_id; string video_path; std::tie(chat_id, video_path) = split(args); send_message(chat_id, make_tl_object(as_local_file(video_path), nullptr, 1, 5)); } else if (op == "svenue") { string chat_id; string latitude; string longitude; string title; string address; string provider; string venue_id; std::tie(chat_id, args) = split(args); std::tie(latitude, args) = split(args); std::tie(longitude, args) = split(args); std::tie(title, args) = split(args); std::tie(address, args) = split(args); std::tie(provider, venue_id) = split(args); send_message(chat_id, make_tl_object(make_tl_object( as_location(latitude, longitude), title, address, provider, venue_id))); } else if (op == "test") { send_request(make_tl_object()); } else if (op == "alarm") { send_request(make_tl_object(to_double(args))); } else if (op == "delete") { string chat_id; string remove_from_the_chat_list; std::tie(chat_id, remove_from_the_chat_list) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_bool(remove_from_the_chat_list))); } else if (op == "dmfu") { string chat_id; string user_id; std::tie(chat_id, user_id) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_user_id(user_id))); } else if (op == "cnbgc") { string user_ids_string; string title; std::tie(user_ids_string, title) = split(args); send_request(make_tl_object(as_user_ids(user_ids_string, ','), title)); } else if (op == "cnch") { send_request(make_tl_object(args, true, "Description")); } else if (op == "cnsg") { send_request(make_tl_object(args, false, "Description")); } else if (op == "UpgradeBasicGroupChatToSupergroupChat") { send_request(make_tl_object(as_chat_id(args))); } else if (op == "DeleteSupergroup") { send_request(make_tl_object(to_integer(args))); } else if (op == "gcpc") { send_request(make_tl_object()); } else if (op == "cpc") { string user_id; string force; std::tie(user_id, force) = split(args); send_request(make_tl_object(as_user_id(user_id), as_bool(force))); } else if (op == "cbgc") { string basic_group_id; string force; std::tie(basic_group_id, force) = split(args); send_request(make_tl_object(to_integer(basic_group_id), as_bool(force))); } else if (op == "csgc" || op == "cchc") { string supergroup_id; string force; std::tie(supergroup_id, force) = split(args); send_request(make_tl_object(to_integer(supergroup_id), as_bool(force))); } else if (op == "sct") { string chat_id; string title; std::tie(chat_id, title) = split(args); send_request(make_tl_object(as_chat_id(chat_id), title)); } else if (op == "scp") { string chat_id; string photo_path; std::tie(chat_id, photo_path) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_local_file(photo_path))); } else if (op == "scpid") { string chat_id; string file_id; std::tie(chat_id, file_id) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_input_file_id(file_id))); } else if (op == "sccd") { string chat_id; string client_data; std::tie(chat_id, client_data) = split(args); send_request(make_tl_object(as_chat_id(chat_id), client_data)); } else if (op == "acm") { string chat_id; string user_id; string forward_limit; std::tie(chat_id, args) = split(args); std::tie(user_id, forward_limit) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_user_id(user_id), to_integer(forward_limit))); } else if (op == "acms") { string chat_id; string user_ids; std::tie(chat_id, user_ids) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_user_ids(user_ids, ','))); } else { op_not_found_count++; } if (op == "scms") { string chat_id; string user_id; string status_str; tl_object_ptr status; std::tie(chat_id, args) = split(args); std::tie(user_id, status_str) = split(args); if (status_str == "admin") { status = make_tl_object(true, true, true, true, true, true, true, true, true); } else if (status_str == "member") { status = make_tl_object(); } else if (status_str == "left") { status = make_tl_object(); } else if (status_str == "banned") { status = make_tl_object(std::numeric_limits::max()); } else if (status_str == "creator") { status = make_tl_object(true); } else if (status_str == "uncreator") { status = make_tl_object(false); } else if (status_str == "admin") { status = make_tl_object(true, true, true, true, true, true, true, true, true); } else if (status_str == "unadmin") { status = make_tl_object(true, false, false, false, false, false, false, false, false); } else if (status_str == "rest") { status = make_tl_object(true, static_cast(60 + std::time(nullptr)), false, false, false, false); } else if (status_str == "restkick") { status = make_tl_object(false, static_cast(60 + std::time(nullptr)), true, false, false, false); } else if (status_str == "unrest") { status = make_tl_object(true, 0, true, true, true, true); } if (status != nullptr) { send_request( make_tl_object(as_chat_id(chat_id), as_user_id(user_id), std::move(status))); } else { LOG(ERROR) << "Unknown status \"" << status_str << "\""; } } else if (op == "log") { string chat_id; string limit; std::tie(chat_id, limit) = split(args); send_request(make_tl_object(as_chat_id(chat_id), "", 0, to_integer(limit), nullptr, vector())); } else if (op == "dcm") { string chat_id; string user_id; std::tie(chat_id, user_id) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_user_id(user_id), make_tl_object())); } else if (op == "sn") { string first_name; string last_name; std::tie(first_name, last_name) = split(args); send_request(make_tl_object(first_name, last_name)); } else if (op == "sb") { send_request(make_tl_object("\n" + args + "\n" + args + "\n")); } else if (op == "sun") { send_request(make_tl_object(args)); } else if (op == "tbga") { string group_id; string everyone_is_administrator; std::tie(group_id, everyone_is_administrator) = split(args); send_request(make_tl_object(to_integer(group_id), as_bool(everyone_is_administrator))); } else if (op == "csgun" || op == "cchun") { string supergroup_id; string username; std::tie(supergroup_id, username) = split(args); send_request(make_tl_object(to_integer(supergroup_id), username)); } else if (op == "ssgss") { string supergroup_id; string sticker_set_id; std::tie(supergroup_id, sticker_set_id) = split(args); send_request(make_tl_object(to_integer(supergroup_id), to_integer(sticker_set_id))); } else if (op == "tsgi") { string supergroup_id; string anyone_can_invite; std::tie(supergroup_id, anyone_can_invite) = split(args); send_request(make_tl_object(to_integer(supergroup_id), as_bool(anyone_can_invite))); } else if (op == "tsgp") { string supergroup_id; string is_all_history_available; std::tie(supergroup_id, is_all_history_available) = split(args); send_request(make_tl_object(to_integer(supergroup_id), as_bool(is_all_history_available))); } else if (op == "csgd" || op == "cchd") { string supergroup_id; string description; std::tie(supergroup_id, description) = split(args); send_request(make_tl_object(to_integer(supergroup_id), description)); } else if (op == "psgm" || op == "pchm") { string supergroup_id; string message_id; std::tie(supergroup_id, message_id) = split(args); send_request(make_tl_object(to_integer(supergroup_id), as_message_id(message_id), false)); } else if (op == "pchms") { string supergroup_id; string message_id; std::tie(supergroup_id, message_id) = split(args); send_request(make_tl_object(to_integer(supergroup_id), as_message_id(message_id), true)); } else if (op == "upsgm" || op == "upchm") { send_request(make_tl_object(to_integer(args))); } else if (op == "grib") { send_request(make_tl_object()); } else if (op == "spc" || op == "su" || op == "sch") { send_request(make_tl_object(args)); } else if (op == "spcs") { send_request(make_tl_object(args)); } else if (op == "sc") { string limit; string query; std::tie(limit, query) = split(args); send_request(make_tl_object(query, to_integer(limit))); } else if (op == "scos") { string limit; string query; std::tie(limit, query) = split(args); send_request(make_tl_object(query, to_integer(limit))); } else if (op == "sco") { string limit; string query; std::tie(limit, query) = split(args); send_request(make_tl_object(query, to_integer(limit))); } else if (op == "arfc") { send_request(make_tl_object(as_chat_id(args))); } else if (op == "rrfc") { send_request(make_tl_object(as_chat_id(args))); } else if (op == "crfcs") { send_request(make_tl_object()); } else if (op == "gwpp") { send_request(make_tl_object(as_caption(args))); } else if (op == "gwpiv") { string url; string force_full; std::tie(url, force_full) = split(args); send_request(make_tl_object(url, as_bool(force_full))); } else if (op == "spp") { send_request(make_tl_object(as_local_file(args))); } else if (op == "sppg") { string path; string conversion; std::tie(path, conversion) = split(args); send_request(make_tl_object(as_generated_file(path, conversion))); } else if (op == "sh") { auto prefix = std::move(args); send_request(make_tl_object(prefix, 10)); } else if (op == "rrh") { auto hashtag = std::move(args); send_request(make_tl_object(hashtag)); } else if (op == "view") { string chat_id; string message_ids_string; std::tie(chat_id, message_ids_string) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_message_ids(message_ids_string), true)); } else if (op == "omc") { string chat_id; string message_id; std::tie(chat_id, message_id) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_message_id(message_id))); } else if (op == "racm") { string chat_id = args; send_request(make_tl_object(as_chat_id(chat_id))); } else if (op == "dpp") { send_request(make_tl_object(to_integer(args))); } else if (op == "gns") { send_request(make_tl_object(get_notification_settings_scope(args))); } else if (op == "sns") { string scope; string settings; std::tie(scope, settings) = split(args); string mute_for; string sound; string show_previews; std::tie(mute_for, settings) = split(settings, ','); std::tie(sound, show_previews) = split(settings, ','); send_request(make_tl_object( get_notification_settings_scope(scope), make_tl_object(to_integer(mute_for), sound, as_bool(show_previews)))); } else if (op == "rans") { send_request(make_tl_object()); } else if (op == "gcrss") { send_request(make_tl_object(as_chat_id(args))); } else if (op == "ccrss") { string chat_id; string is_spam_chat; std::tie(chat_id, is_spam_chat) = split(args); send_request(make_tl_object(as_chat_id(chat_id), as_bool(is_spam_chat))); } else if (op == "rc") { string chat_id; string reason_str; std::tie(chat_id, reason_str) = split(args); tl_object_ptr reason; if (reason_str == "spam") { reason = make_tl_object(); } else if (reason_str == "violence") { reason = make_tl_object(); } else if (reason_str == "porno") { reason = make_tl_object(); } else { reason = make_tl_object(reason_str); } send_request(make_tl_object(as_chat_id(chat_id), std::move(reason))); } else if (op == "rsgs" || op == "rchs") { string supergroup_id; string user_id; string message_ids_string; std::tie(supergroup_id, args) = split(args); std::tie(user_id, message_ids_string) = split(args); send_request(make_tl_object(to_integer(supergroup_id), as_user_id(user_id), as_message_ids(message_ids_string))); } else if (op == "gdiff") { send_request(make_tl_object()); } else if (op == "cproxy") { send_request(make_tl_object(make_tl_object())); } else if (op == "sproxy") { string server; string port; std::tie(server, port) = split(args); send_request(make_tl_object( make_tl_object(server.empty() ? "162.213.174.36" : server, port.empty() ? 13313 : to_integer(port), "sosouser", "SoSo23"))); } else if (op == "gproxy") { send_request(make_tl_object()); } else if (op == "SetVerbosity") { int new_verbosity = to_integer(args); SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL) + new_verbosity); LOG(PLAIN) << "Verbosity=" << new_verbosity; } else if (op == "q" || op == "Quit") { quit(); } else if (op == "dnq" || op == "DumpNetQueries") { dump_pending_network_queries(); } else if (op == "fatal") { LOG(FATAL) << "Fatal!"; } else if (op == "unreachable") { UNREACHABLE(); } else { op_not_found_count++; } if (op_not_found_count == OP_BLOCK_COUNT && !cmd.empty()) { LOG(ERROR) << "Unknown command \"" << cmd << "\" of length " << cmd.size(); } } bool inited_ = false; void loop() override { if (!inited_) { inited_ = true; init(); } #if !TD_WINDOWS #ifdef USE_READLINE if (can_read(stdin_)) { rl_callback_read_char(); stdin_.get_fd().clear_flags(Fd::Read); } #else auto r = stdin_.flush_read(); CHECK(r.is_ok()); while (true) { auto cmd = process_stdin(&stdin_.input_buffer()); if (cmd.is_error()) { break; } add_cmd(cmd.ok().as_slice().str()); } #endif while (!cmd_queue_.empty() && !close_flag_) { auto cmd = std::move(cmd_queue_.front()); cmd_queue_.pop(); on_cmd(std::move(cmd)); } #endif if (ready_to_stop_ && close_flag_ && is_stdin_reader_stopped_) { #ifdef USE_READLINE rl_callback_handler_remove(); #endif Scheduler::instance()->finish(); LOG(WARNING) << "STOP"; stop(); } } void timeout_expired() override { if (close_flag_) { return; } for (auto it = pending_file_generations.begin(); it != pending_file_generations.end();) { auto left_size = it->size - it->local_size; CHECK(left_size > 0); if (it->part_size > left_size) { it->part_size = left_size; } BufferSlice block(it->part_size); FileFd::open(it->source, FileFd::Flags::Read).move_as_ok().pread(block.as_slice(), it->local_size).ensure(); auto open_flags = FileFd::Flags::Write | (it->local_size ? 1 : FileFd::Flags::Truncate | FileFd::Flags::Create); FileFd::open(it->destination, open_flags).move_as_ok().pwrite(block.as_slice(), it->local_size).ensure(); it->local_size += it->part_size; if (it->local_size == it->size) { send_request(make_tl_object(it->id, it->size, it->size)); send_request(make_tl_object(it->id, nullptr)); it = pending_file_generations.erase(it); } else { send_request( make_tl_object(it->id, (it->size + it->local_size) / 2, it->local_size)); ++it; } } if (!pending_file_generations.empty()) { set_timeout_in(0.01); } } void hangup_shared() override { CHECK(get_link_token() == 1); LOG(INFO) << "StdinReader stopped"; is_stdin_reader_stopped_ = true; yield(); } void add_cmd(string cmd) { cmd_queue_.push(std::move(cmd)); } int32 my_id_ = 0; bool use_test_dc_ = false; ActorOwn td_; std::queue cmd_queue_; bool close_flag_ = false; bool ready_to_stop_ = false; bool is_stdin_reader_stopped_ = false; bool get_chat_list_ = false; bool disable_network_ = false; int api_id_ = 0; std::string api_hash_; #if TD_WINDOWS ActorOwn<> stdin_reader_; #endif }; CliClient *CliClient::instance_ = nullptr; void quit() { CliClient::quit_instance(); } static void fail_signal(int sig) { signal_safe_write_signal_number(sig); while (true) { // spin forever to allow debugger to attach } } static void usage() { //TODO: } static void on_fatal_error(const char *error) { std::cerr << "Fatal error: " << error << std::endl; } void main(int argc, char **argv) { ignore_signal(SignalType::HangUp).ensure(); ignore_signal(SignalType::Pipe).ensure(); set_signal_handler(SignalType::Error, fail_signal).ensure(); set_signal_handler(SignalType::Abort, fail_signal).ensure(); td::Log::set_fatal_error_callback(on_fatal_error); const char *locale_name = (std::setlocale(LC_ALL, "fr-FR") == nullptr ? "" : "fr-FR"); std::locale new_locale(locale_name); std::locale::global(new_locale); SCOPE_EXIT { std::locale::global(std::locale::classic()); }; CliLog cli_log; log_interface = &cli_log; td::FileLog file_log; td::TsLog ts_log(&file_log); int new_verbosity_level = VERBOSITY_NAME(INFO); bool use_test_dc = false; bool get_chat_list = false; bool disable_network = false; auto api_id = [](auto x) -> int32 { if (x) { return td::to_integer(Slice(x)); } return 0; }(std::getenv("TD_API_ID")); auto api_hash = [](auto x) -> std::string { if (x) { return x; } return ""; }(std::getenv("TD_API_HASH")); // TODO port OptionsParser to Windows for (int i = 1; i < argc; i++) { if (!std::strcmp(argv[i], "--test")) { use_test_dc = true; } else if (!std::strncmp(argv[i], "-v", 2)) { const char *arg = argv[i] + 2; if (*arg == '\0' && i + 1 < argc) { arg = argv[++i]; } int new_verbosity = 0; if (arg[0] == 'v') { new_verbosity = 1; while (arg[0] == 'v') { new_verbosity++; arg++; } } new_verbosity += to_integer(Slice(arg)); new_verbosity_level = VERBOSITY_NAME(FATAL) + new_verbosity; } else if (!std::strncmp(argv[i], "-l", 2)) { const char *arg = argv[i] + 2; if (*arg == '\0' && i + 1 < argc) { arg = argv[++i]; } if (file_log.init(arg) && file_log.init(arg) && file_log.init(arg)) { log_interface = &ts_log; } } else if (!std::strcmp(argv[i], "-W")) { get_chat_list = true; } else if (!std::strcmp(argv[i], "--disable-network") || !std::strcmp(argv[i], "-n")) { disable_network = true; } else if (!std::strcmp(argv[i], "--api_id")) { if (i + 1 >= argc) { return usage(); } api_id = td::to_integer(Slice(argv[++i])); } else if (!std::strcmp(argv[i], "--api_hash")) { if (i + 1 >= argc) { return usage(); } api_hash = argv[++i]; } } if (api_id == 0 || api_hash == "") { LOG(ERROR) << "You should provide some valid api_id and api_hash"; return usage(); } SET_VERBOSITY_LEVEL(new_verbosity_level); { ConcurrentScheduler scheduler; scheduler.init(4); scheduler .create_actor_unsafe(0, "CliClient", use_test_dc, get_chat_list, disable_network, api_id, api_hash) .release(); scheduler.start(); while (scheduler.run_main(100)) { } scheduler.finish(); } dump_memory_usage(); } } // namespace td int main(int argc, char **argv) { td::main(argc, argv); }