#include "app.h" #include #include #include #include #include "util.h" namespace telegram_tui { namespace { bool starts_with_at(const std::string &text, std::size_t index, const char *prefix) { const std::size_t prefix_size = std::char_traits::length(prefix); return text.compare(index, prefix_size, prefix) == 0; } } // namespace std::int64_t App::resolve_message_ref(const ChatInfo &chat, std::int64_t ref) const { if (ref > 0 && ref <= static_cast(chat.messages.size())) { return chat.messages[static_cast(ref - 1)].id; } for (const auto &message : chat.messages) { if (message.id == ref) { return message.id; } } return 0; } std::tuple App::parse_single_message_command(const std::string &value, const char *prefix) const { if (!starts_with_at(value, 0, prefix)) { return {false, 0, value}; } const std::size_t prefix_size = std::char_traits::length(prefix); std::size_t id_start = prefix_size; while (id_start < value.size() && value[id_start] == ' ') { ++id_start; } std::size_t id_end = id_start; while (id_end < value.size() && std::isdigit(static_cast(value[id_end]))) { ++id_end; } if (id_end == id_start) { return {true, 0, {}}; } std::int64_t parsed_value = 0; try { parsed_value = std::stoll(value.substr(id_start, id_end - id_start)); } catch (...) { return {true, 0, {}}; } while (id_end < value.size() && value[id_end] == ' ') { ++id_end; } const auto chat_id = open_chat_id(); if (!chat_id.has_value()) { return {true, 0, value.substr(id_end)}; } const auto chat_it = chats_.find(*chat_id); if (chat_it == chats_.end()) { return {true, 0, value.substr(id_end)}; } const ChatInfo &chat = chat_it->second; return {true, resolve_message_ref(chat, parsed_value), value.substr(id_end)}; } std::optional> App::parse_message_list_command(const std::string &value, const char *prefix) const { if (!starts_with_at(value, 0, prefix)) { return std::nullopt; } const std::size_t prefix_size = std::char_traits::length(prefix); std::string remainder = value.substr(prefix_size); remainder = trim_copy(std::move(remainder)); if (remainder.empty()) { return std::vector{}; } const auto chat_id = open_chat_id(); if (!chat_id.has_value()) { return std::vector{}; } const auto chat_it = chats_.find(*chat_id); if (chat_it == chats_.end()) { return std::vector{}; } const ChatInfo &chat = chat_it->second; for (char &ch : remainder) { if (ch == ',') { ch = ' '; } } std::set unique_message_ids; std::stringstream stream(remainder); std::string token; while (stream >> token) { const auto dash = token.find('-'); if (dash != std::string::npos) { const std::string start_text = token.substr(0, dash); const std::string end_text = token.substr(dash + 1); if (!is_decimal_number(start_text) || !is_decimal_number(end_text)) { return std::vector{}; } std::int64_t start_value = 0; std::int64_t end_value = 0; try { start_value = std::stoll(start_text); end_value = std::stoll(end_text); } catch (...) { return std::vector{}; } if (start_value > end_value) { std::swap(start_value, end_value); } for (std::int64_t ref = start_value; ref <= end_value; ++ref) { const std::int64_t message_id = resolve_message_ref(chat, ref); if (message_id == 0) { return std::vector{}; } unique_message_ids.insert(message_id); } continue; } if (!is_decimal_number(token)) { return std::vector{}; } std::int64_t parsed_value = 0; try { parsed_value = std::stoll(token); } catch (...) { return std::vector{}; } const std::int64_t message_id = resolve_message_ref(chat, parsed_value); if (message_id == 0) { return std::vector{}; } unique_message_ids.insert(message_id); } return std::vector(unique_message_ids.begin(), unique_message_ids.end()); } App::ComposeCommand App::parse_compose_command(const std::string &value) const { if (const auto message_ids = parse_message_list_command(value, ">f "); message_ids.has_value()) { return ComposeCommand{ComposeCommandKind::Forward, 0, *message_ids, {}}; } if (const auto message_ids = parse_message_list_command(value, ">d "); message_ids.has_value()) { return ComposeCommand{ComposeCommandKind::Delete, 0, *message_ids, {}}; } if (const auto [matched, message_id, text] = parse_single_message_command(value, ">e "); matched) { return ComposeCommand{ComposeCommandKind::Edit, message_id, {}, text}; } if (const auto [matched, message_id, text] = parse_single_message_command(value, ">r "); matched) { return ComposeCommand{ComposeCommandKind::Reply, message_id, {}, text}; } return ComposeCommand{}; } } // namespace telegram_tui