1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2025-02-07 13:27:13 +01:00
SqMod/vendor/DPP/src/dpp/commandhandler.cpp
Sandu Liviu Catalin 4f70f89b78 Basic Discord library layout.
Foundation for the discord library bindings. To be gradually exposed to the script.
2021-09-10 20:13:42 +03:00

360 lines
9.8 KiB
C++

/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/commandhandler.h>
#include <dpp/cache.h>
#include <dpp/cluster.h>
#include <dpp/dispatcher.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <fmt/format.h>
#include <sstream>
namespace dpp {
param_info::param_info(parameter_type t, bool o, const std::string &d, const std::map<std::string, std::string> &opts) : type(t), optional(o), description(d), choices(opts)
{
}
commandhandler::commandhandler(cluster* o, bool auto_hook_events, snowflake application_id) : slash_commands_enabled(false), owner(o), app_id(application_id)
{
if (!application_id && o->me.id) {
app_id = o->me.id;
}
if (auto_hook_events) {
o->on_interaction_create([this](const dpp::interaction_create_t &event) {
this->route(event);
});
o->on_message_create([this](const dpp::message_create_t & event) {
this->route(*event.msg);
});
}
}
commandhandler& commandhandler::set_owner(cluster* o)
{
owner = o;
return *this;
}
commandhandler::~commandhandler()
{
}
commandhandler& commandhandler::add_prefix(const std::string &prefix)
{
prefixes.push_back(prefix);
if (prefix == "/") {
if (!slash_commands_enabled) {
/* Register existing slash commands */
slash_commands_enabled = true;
} else {
slash_commands_enabled = true;
}
}
return *this;
}
commandhandler& commandhandler::add_command(const std::string &command, const parameter_registration_t &parameters, command_handler handler, const std::string &description, snowflake guild_id)
{
command_info_t i;
i.func = handler;
i.guild_id = guild_id;
i.parameters = parameters;
commands[lowercase(command)] = i;
if (slash_commands_enabled) {
if (this->app_id == 0) {
if (owner->me.id == 0) {
throw dpp::exception("Command handler not ready (i don't know my application ID)");
} else {
this->app_id = owner->me.id;
}
}
dpp::slashcommand newcommand;
/* Create a new global command on ready event */
newcommand.set_name(lowercase(command)).set_description(description).set_application_id(this->app_id);
for (auto& parameter : parameters) {
command_option_type cot;
switch (parameter.second.type) {
case pt_boolean:
cot = co_boolean;
break;
case pt_integer:
cot = co_integer;
break;
case pt_string:
cot = co_string;
break;
case pt_user:
cot = co_user;
break;
case pt_role:
cot = co_role;
break;
case pt_channel:
cot = co_channel;
break;
}
command_option opt(cot, parameter.first, parameter.second.description, !parameter.second.optional);
if (!parameter.second.choices.empty()) {
for (auto& c : parameter.second.choices) {
opt.add_choice(dpp::command_option_choice(c.second, c.first));
}
}
newcommand.add_option(opt);
}
/* Register the command */
if (guild_id) {
owner->guild_command_create(newcommand, guild_id, [command, this](const dpp::confirmation_callback_t &callback) {
if (callback.is_error()) {
this->owner->log(dpp::ll_error, fmt::format("Failed to register guild slash command '{}': {}", command, callback.http_info.body));
}
});
} else {
owner->global_command_create(newcommand, [command, this](const dpp::confirmation_callback_t &callback) {
if (callback.is_error()) {
this->owner->log(dpp::ll_error, fmt::format("Failed to register global slash command '{}': {}", command, callback.http_info.body));
}
});
}
}
return *this;
}
bool commandhandler::string_has_prefix(std::string &str)
{
size_t str_length = utility::utf8len(str);
for (auto& p : prefixes) {
size_t prefix_length = utility::utf8len(p);
if (utility::utf8substr(str, 0, prefix_length) == p) {
str.erase(str.begin(), str.begin() + prefix_length);
return true;
}
}
return false;
}
void commandhandler::route(const dpp::message& msg)
{
std::string msg_content = msg.content;
if (string_has_prefix(msg_content)) {
/* Put the string into stringstream to parse parameters at spaces.
* We use stringstream as it handles multiple spaces etc nicely.
*/
std::stringstream ss(msg_content);
std::string command;
ss >> command;
/* Prefixed command, the prefix was removed */
auto found_cmd = commands.find(lowercase(command));
if (found_cmd != commands.end()) {
/* Filter out guild specific commands that are not for the current guild */
if (found_cmd->second.guild_id && found_cmd->second.guild_id != msg.guild_id) {
return;
}
parameter_list_t call_params;
/* Command found; parse parameters */
for (auto& p : found_cmd->second.parameters) {
command_parameter param;
/* Check for end of stream */
if (!ss) {
/* If it's an optional param, we dont care */
if (!p.second.optional) {
/* Trigger missing parameter handler? */
}
break;
}
switch (p.second.type) {
case pt_string: {
std::string x;
ss >> x;
param = x;
}
break;
case pt_role: {
std::string x;
ss >> x;
if (x.length() > 4 && x[0] == '<' && x[1] == '&') {
snowflake rid = from_string<uint64_t>(x.substr(2, x.length() - 1), std::dec);
role* r = dpp::find_role(rid);
if (r) {
param = *r;
}
}
}
break;
case pt_channel: {
std::string x;
ss >> x;
if (x.length() > 4 && x[0] == '<' && x[1] == '#') {
snowflake cid = from_string<uint64_t>(x.substr(2, x.length() - 1), std::dec);
channel* c = dpp::find_channel(cid);
if (c) {
param = *c;
}
}
}
break;
case pt_user: {
std::string x;
ss >> x;
if (x.length() > 4 && x[0] == '<' && x[1] == '@') {
snowflake uid = from_string<uint64_t>(x.substr(2, x.length() - 1), std::dec);
user* u = dpp::find_user(uid);
if (u) {
param = *u;
}
}
}
break;
case pt_integer: {
int32_t x = 0;
ss >> x;
param = x;
}
case pt_boolean: {
std::string x;
bool y = false;
ss >> x;
x = lowercase(x);
if (x == "yes" || x == "1" || x == "true") {
y = true;
}
param = y;
}
break;
}
/* Add parameter to the list */
call_params.push_back(std::make_pair(p.first, param));
}
/* Call command handler */
command_source source;
source.command_id = 0;
source.guild_id = msg.guild_id;
source.channel_id = msg.channel_id;
source.issuer = msg.author;
found_cmd->second.func(command, call_params, source);
}
}
}
void commandhandler::route(const interaction_create_t & event)
{
/* We don't need to check for prefixes here, slash command interactions
* dont have prefixes at all.
*/
command_interaction cmd = std::get<command_interaction>(event.command.data);
auto found_cmd = commands.find(lowercase(cmd.name));
if (found_cmd != commands.end()) {
/* Command found; parse parameters */
parameter_list_t call_params;
for (auto& p : found_cmd->second.parameters) {
command_parameter param;
const command_value& slash_parameter = event.get_parameter(p.first);
if (p.second.optional && slash_parameter.valueless_by_exception()) {
/* Missing optional parameter, skip this */
continue;
}
try {
switch (p.second.type) {
case pt_string: {
std::string s = std::get<std::string>(slash_parameter);
param = s;
}
break;
case pt_role: {
snowflake rid = std::get<snowflake>(slash_parameter);
role* r = dpp::find_role(rid);
if (r) {
param = *r;
}
}
break;
case pt_channel: {
snowflake cid = std::get<snowflake>(slash_parameter);
channel* c = dpp::find_channel(cid);
if (c) {
param = *c;
}
}
break;
case pt_user: {
snowflake uid = std::get<snowflake>(slash_parameter);
user* u = dpp::find_user(uid);
if (u) {
param = *u;
}
}
break;
case pt_integer: {
int32_t i = std::get<int32_t>(slash_parameter);
param = i;
}
case pt_boolean: {
bool b = std::get<bool>(slash_parameter);
param = b;
}
break;
}
}
catch (const std::bad_variant_access& e) {
/* Missing optional parameter, skip this */
continue;
}
/* Add parameter to the list */
call_params.push_back(std::make_pair(p.first, param));
}
/* Call command handler */
command_source source;
source.command_id = event.command.id;
source.command_token = event.command.token;
source.guild_id = event.command.guild_id;
source.channel_id = event.command.channel_id;
source.issuer = (user*)&event.command.usr;
found_cmd->second.func(cmd.name, call_params, source);
}
}
void commandhandler::reply(const dpp::message &m, command_source source)
{
dpp::message msg = m;
msg.guild_id = source.guild_id;
msg.channel_id = source.channel_id;
if (!source.command_token.empty() && source.command_id) {
owner->interaction_response_create(source.command_id, source.command_token, dpp::interaction_response(ir_channel_message_with_source, msg));
} else {
owner->message_create(msg);
}
}
};