From ada1621a841de3e86fdd2d2ef930d595fea1f714 Mon Sep 17 00:00:00 2001 From: ilotterytea Date: Thu, 3 Jul 2025 18:06:10 +0500 Subject: feat: interactive commands --- bot/src/commands/lua.hpp | 22 ++++++- bot/src/handlers.cpp | 148 +++++++++++++++++++++++++++++++++++------------ 2 files changed, 131 insertions(+), 39 deletions(-) (limited to 'bot') diff --git a/bot/src/commands/lua.hpp b/bot/src/commands/lua.hpp index 2516bab..05e9490 100644 --- a/bot/src/commands/lua.hpp +++ b/bot/src/commands/lua.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -15,6 +16,7 @@ #include "config.hpp" #include "cpr/api.h" #include "schemas/user.hpp" +#include "utils/string.hpp" void print_lua_object_type(const sol::object &obj); @@ -112,7 +114,22 @@ namespace bot::command::lua { request, bundle.localization, command::CommandArgument::VALUE); } - std::string url = request.message.value(); + std::vector parts = + utils::string::split_text(request.message.value(), ' '); + + std::string url = parts[0]; + + parts.erase(parts.begin()); + + std::string m = utils::string::join_vector(parts, ' '); + + command::Request r = request; + + if (m.empty()) { + r.message = std::nullopt; + } else { + r.message = m; + } std::vector mimeTypes = {"text/plain", "text/x-lua", "text/plain; charset=utf-8", @@ -139,8 +156,7 @@ namespace bot::command::lua { std::string script = response.text; - return command::lua::run_safe_lua_script(request, bundle, script, - url); + return command::lua::run_safe_lua_script(r, bundle, script, url); } }; } diff --git a/bot/src/handlers.cpp b/bot/src/handlers.cpp index c7bdc36..46ae51d 100644 --- a/bot/src/handlers.cpp +++ b/bot/src/handlers.cpp @@ -5,12 +5,14 @@ #include #include #include +#include #include #include #include "bundle.hpp" #include "commands/command.hpp" #include "commands/request.hpp" +#include "commands/response.hpp" #include "commands/response_error.hpp" #include "constants.hpp" #include "cpr/api.h" @@ -25,62 +27,136 @@ #include "utils/string.hpp" namespace bot::handlers { - void handle_private_message( + std::optional run_command( const InstanceBundle &bundle, command::CommandLoader &command_loader, - const irc::Message &message) { - std::unique_ptr conn = - db::create_connection(bundle.configuration); - + const irc::Message &message, + const command::Requester &requester, + std::unique_ptr &conn) { std::optional request = - command::generate_request(command_loader, message, conn); + command::generate_request(command_loader, message, requester, conn); if (request.has_value()) { try { auto response = command_loader.run(bundle, request.value()); - if (response.has_value()) { - if (response->is_single()) { - bundle.irc_client.say(message.source.login, response->get_single()); - } else if (response->is_multiple()) { - for (const std::string &msg : response->get_multiple()) { - bundle.irc_client.say(message.source.login, msg); - } - } - - return; - } + return response; } catch (const std::exception &e) { bundle.irc_client.say(message.source.login, e.what()); log::error("PrivMsg/" + request->command_id, e.what()); } } - db::DatabaseRows channels = - conn->exec("SELECT * FROM channels WHERE alias_id = $1", - {std::to_string(message.source.id)}); + return std::nullopt; + } + + std::optional handle_custom_commands( + const InstanceBundle &bundle, command::CommandLoader &command_loader, + std::unique_ptr &conn, + const command::Requester &requester, + const irc::Message &message) { + std::vector parts = + utils::string::split_text(message.message, ' '); + + if (parts.empty()) { + return std::nullopt; + } + + std::string cid = parts[0]; + + db::DatabaseRows cmds = conn->exec( + "SELECT message FROM custom_commands WHERE name = $1 AND (channel_id " + "= $2 OR is_global = TRUE)", + {cid, std::to_string(requester.channel.get_id())}); + + if (cmds.empty()) { + return std::nullopt; + } + + parts.erase(parts.begin()); + + std::string initial_message = utils::string::join_vector(parts, ' '); + + db::DatabaseRow cmd = cmds[0]; + std::string msg = cmd.at("message"); + + // parsing values + std::regex pattern(R"(\{([^}]*)\})"); + std::string output; + std::sregex_iterator iter(msg.begin(), msg.end(), pattern); + std::sregex_iterator end; + + int last_pos = 0; + for (; iter != end; ++iter) { + auto m = *iter; + output += msg.substr(last_pos, m.position() - last_pos); + + std::string inside = m[1].str(); + + int placeholder_pos = inside.find("$1"); + if (placeholder_pos != std::string::npos) { + inside.replace(placeholder_pos, 3, initial_message); + } + + irc::Message msg2 = message; + msg2.message = inside; + + std::optional response = + run_command(bundle, command_loader, msg2, requester, conn); + + std::string answer = inside; + + if (response.has_value()) { + if (response->is_single()) { + answer = response->get_single(); + } else if (response->is_multiple()) { + answer = "[multiple strings]"; + } + } + + output += answer; + + last_pos = m.position() + m.length(); + } + + output += msg.substr(last_pos); - if (!channels.empty()) { - schemas::Channel channel(channels[0]); + return output; + } - db::DatabaseRows channel_preferences = conn->exec( - "SELECT * FROM channel_preferences WHERE " - "id = $1", - {std::to_string(channel.get_id())}); + void handle_private_message( + const InstanceBundle &bundle, command::CommandLoader &command_loader, + const irc::Message &message) { + std::unique_ptr conn = + db::create_connection(bundle.configuration); - schemas::ChannelPreferences preference(channel_preferences[0]); + std::optional requester = + command::get_requester(message, conn); - db::DatabaseRows cmds = conn->exec( - "SELECT message FROM custom_commands WHERE name = $1 AND channel_id " - "= $2", - {message.message, std::to_string(channel.get_id())}); + if (!requester.has_value()) { + return; + } - if (!cmds.empty()) { - std::string msg = cmds[0].at("message"); + std::optional response = + run_command(bundle, command_loader, message, *requester, conn); - bundle.irc_client.say(message.source.login, msg); - } else { - make_markov_response(bundle, message, channel, preference); + if (response.has_value()) { + if (response->is_single()) { + bundle.irc_client.say(message.source.login, response->get_single()); + } else if (response->is_multiple()) { + for (const std::string &msg : response->get_multiple()) { + bundle.irc_client.say(message.source.login, msg); + } } + + return; + } + + std::optional custom_command_response = handle_custom_commands( + bundle, command_loader, conn, *requester, message); + + if (custom_command_response.has_value()) { + bundle.irc_client.say(message.source.login, *custom_command_response); + return; } conn->close(); -- cgit v1.2.3