#include "commands/lua.hpp" #include #include #include #include #include #include #include #include "bundle.hpp" #include "commands/request.hpp" #include "commands/response.hpp" #include "commands/response_error.hpp" #include "schemas/user.hpp" #include "utils/chrono.hpp" #include "utils/string.hpp" namespace bot::command::lua { namespace library { void add_bot_library(std::shared_ptr state) { state->set_function("bot_get_compiler_version", []() { std::string info; #ifdef __cplusplus info.append("C++" + std::to_string(__cplusplus).substr(2, 2)); #endif #ifdef __VERSION__ info.append(" (gcc " + bot::utils::string::split_text(__VERSION__, ' ')[0] + ")"); #endif return info; }); state->set_function("bot_get_uptime", []() { auto now = std::chrono::steady_clock::now(); auto duration = now - START_TIME; auto seconds = std::chrono::duration_cast(duration).count(); return static_cast(seconds); }); state->set_function("bot_get_memory_usage", []() { struct rusage usage; getrusage(RUSAGE_SELF, &usage); return usage.ru_maxrss; }); state->set_function("bot_get_compile_time", []() { return BOT_COMPILED_TIMESTAMP; }); state->set_function("bot_get_version", []() { return BOT_VERSION; }); } void add_time_library(std::shared_ptr state) { state->set_function("time_current", []() { return static_cast( std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()) .count()); }); state->set_function("time_humanize", [](const int ×tamp) { return utils::chrono::format_timestamp(timestamp); }); } } std::string parse_lua_response(const sol::table &r, sol::object &res) { if (res.get_type() == sol::type::function) { sol::function f = res.as(); sol::object o = f(r); return parse_lua_response(r, o); } else if (res.get_type() == sol::type::string) { return {"🌑 " + res.as()}; } else if (res.get_type() == sol::type::number) { return {"🌑 " + std::to_string(res.as())}; } else if (res.get_type() == sol::type::boolean) { return {"🌑 " + std::to_string(res.as())}; } else { // should it be ResponseException? return "Empty or unsupported response"; } } command::Response run_safe_lua_script(const Request &request, const InstanceBundle &bundle, const std::string &script) { // shared_ptr is unnecessary here, but my library needs it. std::shared_ptr state = std::make_shared(); state->open_libraries(sol::lib::base, sol::lib::table, sol::lib::string); library::add_bot_library(state); library::add_time_library(state); sol::load_result s = state->load("return " + script); if (!s.valid()) { s = state->load(script); } if (!s.valid()) { sol::error err = s; throw ResponseException( request, bundle.localization, std::string(err.what())); } sol::protected_function_result res = s(); if (!res.valid()) { sol::error err = s; throw ResponseException( request, bundle.localization, std::string(err.what())); } sol::object o = res; return parse_lua_response(request.as_lua_table(state), o); } LuaCommand::LuaCommand(std::shared_ptr luaState, const std::string &script) { this->luaState = luaState; sol::table data = luaState->script(script); this->name = data["name"]; this->delay = data["delay_sec"]; sol::table subcommands = data["subcommands"]; for (auto &k : subcommands) { sol::object value = k.second; if (value.is()) { this->subcommands.push_back(value.as()); } } std::string rights_text = data["minimal_rights"]; if (rights_text == "suspended") { this->level = schemas::PermissionLevel::SUSPENDED; } else if (rights_text == "user") { this->level = schemas::PermissionLevel::USER; } else if (rights_text == "vip") { this->level = schemas::PermissionLevel::VIP; } else if (rights_text == "moderator") { this->level = schemas::PermissionLevel::MODERATOR; } else if (rights_text == "broadcaster") { this->level = schemas::PermissionLevel::BROADCASTER; } else { this->level = schemas::PermissionLevel::USER; } this->handle = data["handle"]; } Response LuaCommand::run(const InstanceBundle &bundle, const Request &request) const { sol::object response = this->handle(request.as_lua_table(this->luaState)); if (response.is()) { return {response.as()}; } else if (response.is()) { sol::table tbl = response.as(); std::vector items; for (auto &kv : tbl) { sol::object value = kv.second; if (value.is()) { items.push_back(value.as()); } } return items; } return {}; } }