181 lines
5.0 KiB
C++
181 lines
5.0 KiB
C++
#include "app.h"
|
|
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
#include <set>
|
|
#include <sstream>
|
|
|
|
#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<char>::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<std::int64_t>(chat.messages.size())) {
|
|
return chat.messages[static_cast<std::size_t>(ref - 1)].id;
|
|
}
|
|
for (const auto &message : chat.messages) {
|
|
if (message.id == ref) {
|
|
return message.id;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
std::tuple<bool, std::int64_t, std::string>
|
|
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<char>::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<unsigned char>(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<std::vector<std::int64_t>> 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<char>::length(prefix);
|
|
std::string remainder = value.substr(prefix_size);
|
|
remainder = trim_copy(std::move(remainder));
|
|
if (remainder.empty()) {
|
|
return std::vector<std::int64_t>{};
|
|
}
|
|
|
|
const auto chat_id = open_chat_id();
|
|
if (!chat_id.has_value()) {
|
|
return std::vector<std::int64_t>{};
|
|
}
|
|
const auto chat_it = chats_.find(*chat_id);
|
|
if (chat_it == chats_.end()) {
|
|
return std::vector<std::int64_t>{};
|
|
}
|
|
|
|
const ChatInfo &chat = chat_it->second;
|
|
for (char &ch : remainder) {
|
|
if (ch == ',') {
|
|
ch = ' ';
|
|
}
|
|
}
|
|
|
|
std::set<std::int64_t> 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>{};
|
|
}
|
|
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<std::int64_t>{};
|
|
}
|
|
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<std::int64_t>{};
|
|
}
|
|
unique_message_ids.insert(message_id);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!is_decimal_number(token)) {
|
|
return std::vector<std::int64_t>{};
|
|
}
|
|
std::int64_t parsed_value = 0;
|
|
try {
|
|
parsed_value = std::stoll(token);
|
|
} catch (...) {
|
|
return std::vector<std::int64_t>{};
|
|
}
|
|
const std::int64_t message_id = resolve_message_ref(chat, parsed_value);
|
|
if (message_id == 0) {
|
|
return std::vector<std::int64_t>{};
|
|
}
|
|
unique_message_ids.insert(message_id);
|
|
}
|
|
|
|
return std::vector<std::int64_t>(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
|