summaryrefslogtreecommitdiff
path: root/bot/src/commands/lua.hpp
blob: 3d514f3bc9294a94da406422f8fcd6ce6d20d5fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#pragma once

#include <memory>
#include <sol/sol.hpp>
#include <sol/state.hpp>
#include <sol/table.hpp>
#include <sol/types.hpp>
#include <string>
#include <vector>

#include "bundle.hpp"
#include "commands/command.hpp"
#include "commands/response.hpp"
#include "commands/response_error.hpp"
#include "config.hpp"
#include "cpr/api.h"
#include "schemas/user.hpp"

void print_lua_object_type(const sol::object &obj);

namespace bot::command::lua {
  namespace library {
    void add_bot_library(std::shared_ptr<sol::state> state);
    void add_bot_library(std::shared_ptr<sol::state> state,
                         const InstanceBundle &bundle);
    void add_time_library(std::shared_ptr<sol::state> state);
    void add_json_library(std::shared_ptr<sol::state> state);
    void add_twitch_library(std::shared_ptr<sol::state> state,
                            const Request &request,
                            const InstanceBundle &bundle);
    void add_net_library(std::shared_ptr<sol::state> state);
    void add_db_library(std::shared_ptr<sol::state> state,
                        const Configuration &config);
    void add_irc_library(std::shared_ptr<sol::state> state,
                         const InstanceBundle &bundle);
    void add_l10n_library(std::shared_ptr<sol::state> state,
                          const InstanceBundle &bundle);

    void add_base_libraries(std::shared_ptr<sol::state> state);
    void add_chat_libraries(std::shared_ptr<sol::state> state,
                            const Request &request,
                            const InstanceBundle &bundle);
  }

  command::Response run_safe_lua_script(const Request &request,
                                        const InstanceBundle &bundle,
                                        const std::string &script);

  class LuaCommand : public Command {
    public:
      LuaCommand(std::shared_ptr<sol::state> luaState,
                 const std::string &content);
      ~LuaCommand() = default;

      Response run(const InstanceBundle &bundle,
                   const Request &request) const override;

      std::string get_name() const override { return this->name; }
      int get_delay_seconds() const override { return this->delay; }
      schemas::PermissionLevel get_permission_level() const override {
        return this->level;
      }
      std::vector<std::string> get_subcommand_ids() const override {
        return this->subcommands;
      }

    private:
      std::string name;
      int delay;
      schemas::PermissionLevel level;
      std::vector<std::string> subcommands;

      sol::function handle;

      std::shared_ptr<sol::state> luaState;
  };

  namespace mod {
    class LuaExecution : public command::Command {
        std::string get_name() const override { return "lua"; }

        int get_delay_seconds() const override { return 1; }

        command::Response run(const InstanceBundle &bundle,
                              const command::Request &request) const override {
          if (!request.message.has_value()) {
            throw ResponseException<ResponseError::NOT_ENOUGH_ARGUMENTS>(
                request, bundle.localization, command::CommandArgument::VALUE);
          }

          std::string script = request.message.value();

          return command::lua::run_safe_lua_script(request, bundle, script);
        }
    };

    class LuaRemoteExecution : public command::Command {
        std::string get_name() const override { return "luaimport"; }

        int get_delay_seconds() const override { return 2; }

        command::Response run(const InstanceBundle &bundle,
                              const command::Request &request) const override {
          if (!request.message.has_value()) {
            throw ResponseException<ResponseError::NOT_ENOUGH_ARGUMENTS>(
                request, bundle.localization, command::CommandArgument::VALUE);
          }

          std::string url = request.message.value();

          std::vector<std::string> mimeTypes = {"text/plain", "text/x-lua"};

          cpr::Response response = cpr::Get(
              cpr::Url{url},
              cpr::Header{
                  {"Accept", utils::string::join_vector(mimeTypes, ',')},
                  {"User-Agent", "https://github.com/ilotterytea/bot"}});

          if (response.status_code != 200) {
            throw ResponseException<ResponseError::EXTERNAL_API_ERROR>(
                request, bundle.localization, response.status_code);
          }

          std::string contentType = response.header["Content-Type"];
          if (!std::any_of(
                  mimeTypes.begin(), mimeTypes.end(),
                  [&contentType](const auto &x) { return x == contentType; })) {
            throw ResponseException<ResponseError::INCORRECT_ARGUMENT>(
                request, bundle.localization, url);
          }

          std::string script = response.text;

          return command::lua::run_safe_lua_script(request, bundle, script);
        }
    };
  }
}