Overhaul a lot, fix the JS implementation

This commit is contained in:
2016-08-10 17:07:41 +01:00
parent f47d8adc36
commit e4cc023055
19 changed files with 1236 additions and 632 deletions

View File

@ -7,150 +7,158 @@
#include "../Logger.hpp"
CommandHelper::CommandHelper() {
sqlite3 *db; int return_code;
return_code = sqlite3_open("bot/db/trivia.db", &db);
namespace CommandHelper {
std::vector<Command> commands;
std::string sql = "SELECT * FROM CustomJS";
void init() {
sqlite3 *db; int return_code;
return_code = sqlite3_open("bot/db/trivia.db", &db);
sqlite3_stmt *stmt;
return_code = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0);
std::string sql = "SELECT * FROM CustomJS";
sqlite3_stmt *stmt;
return_code = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0);
while (return_code != SQLITE_DONE) {
return_code = sqlite3_step(stmt);
while (return_code != SQLITE_DONE) {
return_code = sqlite3_step(stmt);
if (return_code == SQLITE_ROW) {
std::string guild_id = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0));
std::string command_name = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 1));
std::string script = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 2));
if (return_code == SQLITE_ROW) {
std::string guild_id = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0));
std::string command_name = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 1));
std::string script = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 2));
commands.push_back({ guild_id, command_name, script });
}
else if (return_code != SQLITE_DONE) {
sqlite3_finalize(stmt);
std::cerr << "SQLite error." << std::endl;
return;
}
}
Logger::write(std::to_string(commands.size()) + " custom command(s) loaded", Logger::LogLevel::Info);
sqlite3_finalize(stmt);
sqlite3_close(db);
}
bool CommandHelper::get_command(std::string guild_id, std::string command_name, Command &command) {
auto check_lambda = [guild_id, command_name](const Command &c) {
return guild_id == c.guild_id && command_name == c.command_name;
};
auto it = std::find_if(commands.begin(), commands.end(), check_lambda);
if (it == commands.end()) {
command = {};
return false;
} else {
command = { it->guild_id, it->command_name, it->script };
}
}
// returns: 0 error, 1 inserted, 2 updated
int CommandHelper::insert_command(std::string guild_id, std::string command_name, std::string script) {
// TODO: if script empty, delete command
Command command { guild_id, command_name, script };
int ret_value;
std::string sql;
if (command_in_db(guild_id, command_name)) {
sql = "UPDATE CustomJS SET Script=?1 WHERE GuildID=?2 AND CommandName=?3;";
std::cout << "Command already exists, updating." << std::endl;
ret_value = 2;
} else {
sql = "INSERT INTO CustomJS(Script, GuildID, CommandName) VALUES (?1, ?2, ?3);";
std::cout << "Inserting new command." << std::endl;
ret_value = 1;
}
sqlite3 *db; int return_code;
return_code = sqlite3_open("bot/db/trivia.db", &db);
if (!return_code_ok(return_code)) return 0;
sqlite3_stmt *stmt;
return_code = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0);
if (!return_code_ok(return_code)) return 0;
return_code = sqlite3_bind_text(stmt, 1, script.c_str(), -1, (sqlite3_destructor_type)-1);
if (!return_code_ok(return_code)) return 0;
return_code = sqlite3_bind_text(stmt, 2, guild_id.c_str(), -1, (sqlite3_destructor_type)-1);
if (!return_code_ok(return_code)) return 0;
return_code = sqlite3_bind_text(stmt, 3, command_name.c_str(), -1, (sqlite3_destructor_type)-1);
if (!return_code_ok(return_code)) return 0;
return_code = sqlite3_step(stmt);
bool success = return_code == SQLITE_DONE;
sqlite3_finalize(stmt);
sqlite3_close(db);
if (success) {
if (ret_value == 1) {
commands.push_back({ guild_id, command_name, script });
}
if (ret_value == 2) {
// update command, don't add
auto check_lambda = [guild_id, command_name](const Command &c) {
return guild_id == c.guild_id && command_name == c.command_name;
};
auto it = std::find_if(commands.begin(), commands.end(), check_lambda);
if (it == commands.end()) {
return 0;
} else {
it->script = script;
commands.push_back({ guild_id, command_name, script });
}
else if (return_code != SQLITE_DONE) {
sqlite3_finalize(stmt);
std::cerr << "SQLite error." << std::endl;
return;
}
}
return ret_value;
Logger::write(std::to_string(commands.size()) + " custom command(s) loaded", Logger::LogLevel::Info);
sqlite3_finalize(stmt);
sqlite3_close(db);
}
return 0;
}
bool CommandHelper::command_in_db(std::string guild_id, std::string command_name) {
sqlite3 *db; int return_code;
return_code = sqlite3_open("bot/db/trivia.db", &db);
if (!return_code_ok(return_code)) return false;
std::string sql = "SELECT EXISTS(SELECT 1 FROM CustomJS WHERE GuildID=?1 AND CommandName=?2);";
sqlite3_stmt *stmt;
return_code = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0);
if (!return_code_ok(return_code)) return false;
return_code = sqlite3_bind_text(stmt, 1, guild_id.c_str(), -1, (sqlite3_destructor_type) -1);
if (!return_code_ok(return_code)) return false;
return_code = sqlite3_bind_text(stmt, 2, command_name.c_str(), -1, (sqlite3_destructor_type) -1);
if (!return_code_ok(return_code)) return false;
sqlite3_step(stmt);
bool exists = sqlite3_column_int(stmt, 0) == 1; // returns 1 (true) if exists
sqlite3_finalize(stmt);
sqlite3_close(db);
return exists;
}
bool CommandHelper::return_code_ok(int return_code) {
// TODO: NotLikeThis
if (return_code != SQLITE_OK) {
Logger::write("SQLite error", Logger::LogLevel::Severe);
return false;
bool return_code_ok(int return_code) {
// TODO: NotLikeThis
if (return_code != SQLITE_OK) {
Logger::write("SQLite error", Logger::LogLevel::Severe);
return false;
}
return true;
}
bool get_command(std::string guild_id, std::string command_name, Command &command) {
auto check_lambda = [guild_id, command_name](const Command &c) {
return guild_id == c.guild_id && command_name == c.command_name;
};
auto it = std::find_if(commands.begin(), commands.end(), check_lambda);
if (it == commands.end()) {
command = {};
return false;
}
else {
command = { it->guild_id, it->command_name, it->script };
return true;
}
}
// returns: 0 error, 1 inserted, 2 updated
int insert_command(std::string guild_id, std::string command_name, std::string script) {
// TODO: if script empty, delete command
Command command{ guild_id, command_name, script };
int ret_value;
std::string sql;
if (command_in_db(guild_id, command_name)) {
sql = "UPDATE CustomJS SET Script=?1 WHERE GuildID=?2 AND CommandName=?3;";
Logger::write("Command already exists, updating.", Logger::LogLevel::Debug);
ret_value = 2;
}
else {
sql = "INSERT INTO CustomJS(Script, GuildID, CommandName) VALUES (?1, ?2, ?3);";
Logger::write("Inserting new command.", Logger::LogLevel::Debug);
ret_value = 1;
}
sqlite3 *db; int return_code;
return_code = sqlite3_open("bot/db/trivia.db", &db);
if (!return_code_ok(return_code)) return 0;
sqlite3_stmt *stmt;
return_code = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0);
if (!return_code_ok(return_code)) return 0;
return_code = sqlite3_bind_text(stmt, 1, script.c_str(), -1, (sqlite3_destructor_type)-1);
if (!return_code_ok(return_code)) return 0;
return_code = sqlite3_bind_text(stmt, 2, guild_id.c_str(), -1, (sqlite3_destructor_type)-1);
if (!return_code_ok(return_code)) return 0;
return_code = sqlite3_bind_text(stmt, 3, command_name.c_str(), -1, (sqlite3_destructor_type)-1);
if (!return_code_ok(return_code)) return 0;
return_code = sqlite3_step(stmt);
bool success = return_code == SQLITE_DONE;
sqlite3_finalize(stmt);
sqlite3_close(db);
if (success) {
if (ret_value == 1) {
commands.push_back({ guild_id, command_name, script });
}
if (ret_value == 2) {
// update command, don't add
auto check_lambda = [guild_id, command_name](const Command &c) {
return guild_id == c.guild_id && command_name == c.command_name;
};
auto it = std::find_if(commands.begin(), commands.end(), check_lambda);
if (it == commands.end()) {
return 0;
}
else {
it->script = script;
}
}
return ret_value;
}
return 0;
}
bool command_in_db(std::string guild_id, std::string command_name) {
sqlite3 *db; int return_code;
return_code = sqlite3_open("bot/db/trivia.db", &db);
if (!return_code_ok(return_code)) return false;
std::string sql = "SELECT EXISTS(SELECT 1 FROM CustomJS WHERE GuildID=?1 AND CommandName=?2);";
sqlite3_stmt *stmt;
return_code = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0);
if (!return_code_ok(return_code)) return false;
return_code = sqlite3_bind_text(stmt, 1, guild_id.c_str(), -1, (sqlite3_destructor_type)-1);
if (!return_code_ok(return_code)) return false;
return_code = sqlite3_bind_text(stmt, 2, command_name.c_str(), -1, (sqlite3_destructor_type)-1);
if (!return_code_ok(return_code)) return false;
sqlite3_step(stmt);
bool exists = sqlite3_column_int(stmt, 0) == 1; // returns 1 (true) if exists
sqlite3_finalize(stmt);
sqlite3_close(db);
return exists;
}
return true;
}

View File

@ -3,23 +3,17 @@
#include <vector>
struct Command {
std::string guild_id;
std::string command_name;
std::string script;
};
namespace CommandHelper {
struct Command {
std::string guild_id;
std::string command_name;
std::string script;
};
class CommandHelper {
public:
CommandHelper();
void init();
int insert_command(std::string guild_id, std::string command_name, std::string script);
bool get_command(std::string guild_id, std::string name, Command &command);
private:
bool command_in_db(std::string guild_id, std::string command_name);
bool return_code_ok(int return_code);
std::vector<Command> commands;
};
}
#endif

View File

@ -1,15 +1,17 @@
#include <iostream>
#include <string>
#include <chrono>
#include <algorithm>
#include "V8Instance.hpp"
#include "../APIHelper.hpp"
#include "../DiscordAPI.hpp"
#include "../Logger.hpp"
V8Instance::V8Instance(std::string guild_id, std::shared_ptr<APIHelper> ah, std::map<std::string, DiscordObjects::Guild> *guilds, std::map<std::string, DiscordObjects::Channel> *channels,
V8Instance::V8Instance(std::string guild_id, std::map<std::string, DiscordObjects::Guild> *guilds, std::map<std::string, DiscordObjects::Channel> *channels,
std::map<std::string, DiscordObjects::User> *users, std::map<std::string, DiscordObjects::Role> *roles) {
rng = std::mt19937(std::random_device()());
this->guild_id = guild_id;
this->ah = ah;
this->guilds = guilds;
this->channels = channels;
this->users = users;
@ -35,96 +37,645 @@ void V8Instance::create() {
// set global context
Local<Context> context = create_context();
context->Enter();
context_.Reset(isolate, context);
Context::Scope context_scope(context);
initialise(context);
Logger::write("[v8] Created context and context scope", Logger::LogLevel::Debug);
}
void V8Instance::initialise(Local<Context> context) {
HandleScope handle_scope(isolate);
Local<Object> opts_obj = wrap_server(&(*guilds)[guild_id]);
context->Global()->Set(
context,
String::NewFromUtf8(isolate, "server", NewStringType::kNormal).ToLocalChecked(),
opts_obj
).FromJust();
Logger::write("[v8] Bound server template", Logger::LogLevel::Debug);
}
v8::Local<v8::Context> V8Instance::create_context() {
Local<ObjectTemplate> global = ObjectTemplate::New(isolate);
// bind print() function
Local<External> self = External::New(isolate, (void *) this);
Local<External> self = External::New(isolate, (void *) this);
global->Set(
String::NewFromUtf8(isolate, "print", NewStringType::kNormal).ToLocalChecked(),
String::NewFromUtf8(isolate, "print", NewStringType::kNormal).ToLocalChecked(),
FunctionTemplate::New(isolate, V8Instance::js_print, self)
);
global->SetAccessor(
String::NewFromUtf8(isolate, "server", NewStringType::kNormal).ToLocalChecked(),
V8Instance::js_get_server,
(AccessorSetterCallback) 0,
self
global->Set(
String::NewFromUtf8(isolate, "random", NewStringType::kNormal).ToLocalChecked(),
FunctionTemplate::New(isolate, V8Instance::js_random, self)
);
global->SetAccessor(
String::NewFromUtf8(isolate, "channel", NewStringType::kNormal).ToLocalChecked(),
V8Instance::js_get_channel,
(AccessorSetterCallback) 0,
self
);
global->SetAccessor(
String::NewFromUtf8(isolate, "user", NewStringType::kNormal).ToLocalChecked(),
V8Instance::js_get_user,
(AccessorSetterCallback) 0,
self
);
global->SetAccessor(
String::NewFromUtf8(isolate, "input", NewStringType::kNormal).ToLocalChecked(),
V8Instance::js_get_input,
(AccessorSetterCallback) 0,
self
global->Set(
String::NewFromUtf8(isolate, "shuffle", NewStringType::kNormal).ToLocalChecked(),
FunctionTemplate::New(isolate, V8Instance::js_shuffle, self)
);
Logger::write("[v8] Created global obj, linked data and functions", Logger::LogLevel::Debug);
Logger::write("[v8] Created global context, added print function", Logger::LogLevel::Debug);
return Context::New(isolate, NULL, global);
}
void V8Instance::js_get_server(Local<String> property, const PropertyCallbackInfo<Value> &info) {
auto data = info.Data().As<External>();
V8Instance *self = static_cast<V8Instance *>(data->Value());
/* server */
Local<ObjectTemplate> V8Instance::make_server_template() {
EscapableHandleScope handle_scope(isolate);
Local<Object> obj = Object::New(info.GetIsolate());
self->add_to_obj(obj, (*self->guilds)[self->guild_id]);
info.GetReturnValue().Set(obj);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->SetInternalFieldCount(1);
templ->SetHandler(
NamedPropertyHandlerConfiguration(
V8Instance::js_get_server,
(GenericNamedPropertySetterCallback) 0,
(GenericNamedPropertyQueryCallback) 0,
(GenericNamedPropertyDeleterCallback) 0,
(GenericNamedPropertyEnumeratorCallback) 0,
External::New(isolate, (void *) this)
)
);
return handle_scope.Escape(templ);
}
void V8Instance::js_get_channel(Local<String> property, const PropertyCallbackInfo<Value> &info) {
auto data = info.Data().As<External>();
V8Instance *self = static_cast<V8Instance *>(data->Value());
Local<Object> V8Instance::wrap_server(DiscordObjects::Guild *guild) {
EscapableHandleScope handle_scope(isolate);
if (!self->current_channel) {
Logger::write("[v8] current_channel is null pointer", Logger::LogLevel::Severe);
info.GetReturnValue().SetNull();
return;
if (server_template.IsEmpty()) {
Local<ObjectTemplate> raw_template = make_server_template();
server_template.Reset(isolate, raw_template);
}
Local<Object> obj = Object::New(info.GetIsolate());
self->add_to_obj(obj, (*self->current_channel));
info.GetReturnValue().Set(obj);
Local<ObjectTemplate> templ = Local<ObjectTemplate>::New(isolate, server_template);
Local<Object> result = templ->NewInstance(isolate->GetCurrentContext()).ToLocalChecked();
Local<External> guild_ptr = External::New(isolate, guild);
result->SetInternalField(0, guild_ptr);
return handle_scope.Escape(result);
}
void V8Instance::js_get_user(Local<String> property, const PropertyCallbackInfo<Value> &info) {
auto data = info.Data().As<External>();
V8Instance *self = static_cast<V8Instance *>(data->Value());
if (!self->current_sender) {
Logger::write("[v8] current_sender is null pointer", Logger::LogLevel::Severe);
info.GetReturnValue().SetNull();
void V8Instance::js_get_server(Local<Name> property, const PropertyCallbackInfo<Value> &info) {
void *self_v = info.Data().As<External>()->Value();
if (!self_v) {
Logger::write("[v8] [js_get_server] Class pointer empty", Logger::LogLevel::Warning);
return;
}
V8Instance *self = static_cast<V8Instance *>(self_v);
Local<Object> obj = Object::New(info.GetIsolate());
self->add_to_obj(obj, (*self->current_sender));
info.GetReturnValue().Set(obj);
void *guild_v = info.Holder()->GetInternalField(0).As<External>()->Value();
if (!guild_v) {
Logger::write("[v8] [js_get_server] Guild pointer empty", Logger::LogLevel::Warning);
return;
}
DiscordObjects::Guild *guild = static_cast<DiscordObjects::Guild *>(guild_v);
if (!property->IsString()) {
return;
}
std::string property_s = *String::Utf8Value(property);
if (property_s == "Id") {
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), guild->id.c_str(), NewStringType::kNormal).ToLocalChecked());
}
else if (property_s == "Name") {
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), guild->name.c_str(), NewStringType::kNormal).ToLocalChecked());
}
else if (property_s == "IconUrl") {
std::string icon_url = "https://discordapp.com/api/guilds/" + guild->id + "/icons/" + guild->icon + ".jpg";
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), icon_url.c_str(), NewStringType::kNormal).ToLocalChecked());
}
else if (property_s == "Owner") {
std::string owner_id = guild->owner_id;
DiscordObjects::GuildMember *owner = *std::find_if(guild->members.begin(), guild->members.end(), [owner_id](DiscordObjects::GuildMember *m) {
return owner_id == m->user->id;
});
Local<Object> owner_obj = self->wrap_user(owner);
info.GetReturnValue().Set(owner_obj);
}
else if (property_s == "Roles") {
Local<Object> roles_obj = self->wrap_role_list(&guild->roles);
info.GetReturnValue().Set(roles_obj);
}
else if (property_s == "Channels") {
Local<Object> channels_obj = self->wrap_channel_list(&guild->channels);
info.GetReturnValue().Set(channels_obj);
}
else if (property_s == "Users") {
Local<Object> users_obj = self->wrap_user_list(&guild->members);
info.GetReturnValue().Set(users_obj);
}
}
void V8Instance::js_get_input(Local<String> property, const PropertyCallbackInfo<Value> &info) {
auto data = info.Data().As<External>();
/* channel */
Local<ObjectTemplate> V8Instance::make_channel_template() {
EscapableHandleScope handle_scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->SetInternalFieldCount(1);
templ->SetHandler(
NamedPropertyHandlerConfiguration(
V8Instance::js_get_channel,
(GenericNamedPropertySetterCallback) 0,
(GenericNamedPropertyQueryCallback) 0,
(GenericNamedPropertyDeleterCallback) 0,
(GenericNamedPropertyEnumeratorCallback) 0,
External::New(isolate, (void *) this)
)
);
return handle_scope.Escape(templ);
}
Local<Object> V8Instance::wrap_channel(DiscordObjects::Channel *channel) {
EscapableHandleScope handle_scope(isolate);
if (role_template.IsEmpty()) {
Local<ObjectTemplate> raw_template = make_channel_template();
channel_template.Reset(isolate, raw_template);
}
Local<ObjectTemplate> templ = Local<ObjectTemplate>::New(isolate, channel_template);
Local<Object> result = templ->NewInstance(isolate->GetCurrentContext()).ToLocalChecked();
Local<External> channel_ptr = External::New(isolate, channel);
result->SetInternalField(0, channel_ptr);
return handle_scope.Escape(result);
}
void V8Instance::js_get_channel(Local<Name> property, const PropertyCallbackInfo<Value> &info) {
void *self_v = info.Data().As<External>()->Value();
if (!self_v) {
Logger::write("[v8] [js_get_channel] Class pointer empty", Logger::LogLevel::Warning);
return;
}
V8Instance *self = static_cast<V8Instance *>(self_v);
void *channel_v = info.Holder()->GetInternalField(0).As<External>()->Value();
if (!channel_v) {
Logger::write("[v8] [js_get_channel] Channel pointer empty", Logger::LogLevel::Warning);
return;
}
DiscordObjects::Channel *channel = static_cast<DiscordObjects::Channel *>(channel_v);
if (!property->IsString()) {
return;
}
std::string property_s = *String::Utf8Value(property);
if (property_s == "Id") {
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), channel->id.c_str(), NewStringType::kNormal).ToLocalChecked());
}
else if (property_s == "Name") {
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), channel->name.c_str(), NewStringType::kNormal).ToLocalChecked());
}
else if (property_s == "Topic") {
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), channel->topic.c_str(), NewStringType::kNormal).ToLocalChecked());
}
else if (property_s == "IsVoice") {
info.GetReturnValue().Set(Boolean::New(info.GetIsolate(), channel->type == "voice"));
}
else if (property_s == "Users") {
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Channel.Users not implemented.", NewStringType::kNormal).ToLocalChecked());
}
}
/* channel list */
Local<ObjectTemplate> V8Instance::make_channel_list_template() {
EscapableHandleScope handle_scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->SetInternalFieldCount(1);
templ->SetHandler(
IndexedPropertyHandlerConfiguration(
V8Instance::js_get_channel_list,
(IndexedPropertySetterCallback) 0,
(IndexedPropertyQueryCallback) 0,
(IndexedPropertyDeleterCallback) 0,
(IndexedPropertyEnumeratorCallback) 0,
External::New(isolate, (void *) this)
)
);
return handle_scope.Escape(templ);
}
Local<Object> V8Instance::wrap_channel_list(std::vector<DiscordObjects::Channel *> *channel_list) {
EscapableHandleScope handle_scope(isolate);
if (channel_list_template.IsEmpty()) {
Local<ObjectTemplate> raw_template = make_channel_list_template();
channel_list_template.Reset(isolate, raw_template);
}
Local<ObjectTemplate> templ = Local<ObjectTemplate>::New(isolate, channel_list_template);
Local<Object> result = templ->NewInstance(isolate->GetCurrentContext()).ToLocalChecked();
// imitate an array
result->Set(String::NewFromUtf8(isolate, "length", NewStringType::kNormal).ToLocalChecked(), Integer::New(isolate, (*channel_list).size()));
result->SetPrototype(Array::New(isolate)->GetPrototype());
Local<External> channel_list_ptr = External::New(isolate, channel_list);
result->SetInternalField(0, channel_list_ptr);
return handle_scope.Escape(result);
}
void V8Instance::js_get_channel_list(uint32_t index, const PropertyCallbackInfo<Value> &info) {
void *self_v = info.Data().As<External>()->Value();
if (!self_v) {
Logger::write("[v8] [js_get_channel_list] Class pointer empty", Logger::LogLevel::Warning);
return;
}
V8Instance *self = static_cast<V8Instance *>(self_v);
void *channel_list_v = info.Holder()->GetInternalField(0).As<External>()->Value();
if (!channel_list_v) {
Logger::write("[v8] [js_get_channel_list] Channel List pointer empty", Logger::LogLevel::Warning);
return;
}
std::vector<DiscordObjects::Channel *> *channel_list = static_cast<std::vector<DiscordObjects::Channel *> *>(channel_list_v);
if (index < (*channel_list).size()) {
Local<Object> channel_obj = self->wrap_channel((*channel_list)[index]);
info.GetReturnValue().Set(channel_obj);
}
else {
info.GetReturnValue().SetUndefined();
}
}
/* user */
Local<ObjectTemplate> V8Instance::make_user_template() {
EscapableHandleScope handle_scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->SetInternalFieldCount(1);
templ->SetHandler(
NamedPropertyHandlerConfiguration(
V8Instance::js_get_user,
(GenericNamedPropertySetterCallback)0,
(GenericNamedPropertyQueryCallback)0,
(GenericNamedPropertyDeleterCallback)0,
(GenericNamedPropertyEnumeratorCallback)0,
External::New(isolate, (void *) this)
)
);
return handle_scope.Escape(templ);
}
Local<Object> V8Instance::wrap_user(DiscordObjects::GuildMember *member) {
EscapableHandleScope handle_scope(isolate);
if (user_template.IsEmpty()) {
Local<ObjectTemplate> raw_template = make_user_template();
user_template.Reset(isolate, raw_template);
}
Local<ObjectTemplate> templ = Local<ObjectTemplate>::New(isolate, user_template);
Local<Object> result = templ->NewInstance(isolate->GetCurrentContext()).ToLocalChecked();
Local<External> member_ptr = External::New(isolate, member);
result->SetInternalField(0, member_ptr);
return handle_scope.Escape(result);
}
void V8Instance::js_get_user(Local<Name> property, const PropertyCallbackInfo<Value> &info) {
void *self_v = info.Data().As<External>()->Value();
if (!self_v) {
Logger::write("[v8] [js_get_user] Class pointer empty", Logger::LogLevel::Warning);
return;
}
V8Instance *self = static_cast<V8Instance *>(self_v);
void *member_v = info.Holder()->GetInternalField(0).As<External>()->Value();
if (!member_v) {
Logger::write("[v8] [js_get_user] GuildMember pointer empty", Logger::LogLevel::Warning);
return;
}
DiscordObjects::GuildMember *member = static_cast<DiscordObjects::GuildMember *>(member_v);
if (!property->IsString()) {
return;
}
std::string property_s = *String::Utf8Value(property);
if (property_s == "Id") {
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), member->user->id.c_str(), NewStringType::kNormal).ToLocalChecked());
}
else if (property_s == "Name") {
std::string name = member->nick == "null" ? member->user->username : member->nick;
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), name.c_str(), NewStringType::kNormal).ToLocalChecked());
}
else if (property_s == "Mention") {
std::string mention = "<@" + member->user->id + ">";
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), mention.c_str(), NewStringType::kNormal).ToLocalChecked());
}
else if (property_s == "AvatarUrl") {
std::string avatar_url = "https://discordapp.com/api/users/" + member->user->id + "/avatars/" + member->user->avatar + ".jpg";
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), avatar_url.c_str(), NewStringType::kNormal).ToLocalChecked());
}
else if (property_s == "Roles") {
Local<Object> roles_obj = self->wrap_role_list(&member->roles);
info.GetReturnValue().Set(roles_obj);
}
else if (property_s == "State") {
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), member->user->status.c_str(), NewStringType::kNormal).ToLocalChecked());
}
else if (property_s == "CurrentGame") {
if (member->user->game == "null") {
info.GetReturnValue().SetNull();
} else {
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), member->user->game.c_str(), NewStringType::kNormal).ToLocalChecked());
}
}
}
/* user list */
Local<ObjectTemplate> V8Instance::make_user_list_template() {
EscapableHandleScope handle_scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->SetInternalFieldCount(1);
templ->SetHandler(
IndexedPropertyHandlerConfiguration(
V8Instance::js_get_user_list,
(IndexedPropertySetterCallback) 0,
(IndexedPropertyQueryCallback) 0,
(IndexedPropertyDeleterCallback) 0,
(IndexedPropertyEnumeratorCallback) 0,
External::New(isolate, (void *) this)
)
);
return handle_scope.Escape(templ);
}
Local<Object> V8Instance::wrap_user_list(std::vector<DiscordObjects::GuildMember *> *user_list) {
EscapableHandleScope handle_scope(isolate);
if (user_list_template.IsEmpty()) {
Local<ObjectTemplate> raw_template = make_user_list_template();
user_list_template.Reset(isolate, raw_template);
}
Local<ObjectTemplate> templ = Local<ObjectTemplate>::New(isolate, user_list_template);
Local<Object> result = templ->NewInstance(isolate->GetCurrentContext()).ToLocalChecked();
// imitate an array
result->Set(String::NewFromUtf8(isolate, "length", NewStringType::kNormal).ToLocalChecked(), Integer::New(isolate, (*user_list).size()));
result->SetPrototype(Array::New(isolate)->GetPrototype());
Local<External> user_list_ptr = External::New(isolate, user_list);
result->SetInternalField(0, user_list_ptr);
return handle_scope.Escape(result);
}
void V8Instance::js_get_user_list(uint32_t index, const PropertyCallbackInfo<Value> &info) {
void *self_v = info.Data().As<External>()->Value();
if (!self_v) {
Logger::write("[v8] [js_get_user_list] Class pointer empty", Logger::LogLevel::Warning);
return;
}
V8Instance *self = static_cast<V8Instance *>(self_v);
void *user_list_v = info.Holder()->GetInternalField(0).As<External>()->Value();
if (!user_list_v) {
Logger::write("[v8] [js_get_user_list] GuildMember List pointer empty", Logger::LogLevel::Warning);
return;
}
std::vector<DiscordObjects::GuildMember *> *user_list = static_cast<std::vector<DiscordObjects::GuildMember *> *>(user_list_v);
if (index < (*user_list).size()) {
Local<Object> role_obj = self->wrap_user((*user_list)[index]);
info.GetReturnValue().Set(role_obj);
}
else {
info.GetReturnValue().SetUndefined();
}
}
/* role */
Local<ObjectTemplate> V8Instance::make_role_template() {
EscapableHandleScope handle_scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->SetInternalFieldCount(1);
templ->SetHandler(
NamedPropertyHandlerConfiguration(
V8Instance::js_get_role,
(GenericNamedPropertySetterCallback)0,
(GenericNamedPropertyQueryCallback)0,
(GenericNamedPropertyDeleterCallback)0,
(GenericNamedPropertyEnumeratorCallback)0,
External::New(isolate, (void *) this)
)
);
return handle_scope.Escape(templ);
}
Local<Object> V8Instance::wrap_role(DiscordObjects::Role *role) {
EscapableHandleScope handle_scope(isolate);
if (role_template.IsEmpty()) {
Local<ObjectTemplate> raw_template = make_role_template();
role_template.Reset(isolate, raw_template);
}
Local<ObjectTemplate> templ = Local<ObjectTemplate>::New(isolate, role_template);
Local<Object> result = templ->NewInstance(isolate->GetCurrentContext()).ToLocalChecked();
Local<External> role_ptr = External::New(isolate, role);
result->SetInternalField(0, role_ptr);
return handle_scope.Escape(result);
}
void V8Instance::js_get_role(Local<Name> property, const PropertyCallbackInfo<Value> &info) {
void *self_v = info.Data().As<External>()->Value();
if (!self_v) {
Logger::write("[v8] [js_get_role] Class pointer empty", Logger::LogLevel::Warning);
return;
}
V8Instance *self = static_cast<V8Instance *>(self_v);
void *role_v = info.Holder()->GetInternalField(0).As<External>()->Value();
if (!role_v) {
Logger::write("[v8] [js_get_role] Role pointer empty", Logger::LogLevel::Warning);
return;
}
DiscordObjects::Role *role = static_cast<DiscordObjects::Role *>(role_v);
if (!property->IsString()) {
return;
}
std::string property_s = *String::Utf8Value(property);
if (property_s == "Id") {
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), role->id.c_str(), NewStringType::kNormal).ToLocalChecked());
}
else if (property_s == "Name") {
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), role->name.c_str(), NewStringType::kNormal).ToLocalChecked());
}
else if (property_s == "Position") {
info.GetReturnValue().Set(Integer::New(info.GetIsolate(), role->position));
}
else if (property_s == "Red" || property_s == "Green" || property_s == "Blue") {
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Role.[Colour] not implemented.", NewStringType::kNormal).ToLocalChecked());
}
}
/* role list */
Local<ObjectTemplate> V8Instance::make_role_list_template() {
EscapableHandleScope handle_scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->SetInternalFieldCount(1);
templ->SetHandler(
IndexedPropertyHandlerConfiguration(
V8Instance::js_get_role_list,
(IndexedPropertySetterCallback) 0,
(IndexedPropertyQueryCallback) 0,
(IndexedPropertyDeleterCallback) 0,
(IndexedPropertyEnumeratorCallback) 0,
External::New(isolate, (void *) this)
)
);
return handle_scope.Escape(templ);
}
Local<Object> V8Instance::wrap_role_list(std::vector<DiscordObjects::Role *> *role_list) {
EscapableHandleScope handle_scope(isolate);
if (role_list_template.IsEmpty()) {
Local<ObjectTemplate> raw_template = make_role_list_template();
role_list_template.Reset(isolate, raw_template);
}
Local<ObjectTemplate> templ = Local<ObjectTemplate>::New(isolate, role_list_template);
Local<Object> result = templ->NewInstance(isolate->GetCurrentContext()).ToLocalChecked();
// imitate an array
result->Set(String::NewFromUtf8(isolate, "length", NewStringType::kNormal).ToLocalChecked(), Integer::New(isolate, (*role_list).size()));
result->SetPrototype(Array::New(isolate)->GetPrototype());
Local<External> role_list_ptr = External::New(isolate, role_list);
result->SetInternalField(0, role_list_ptr);
return handle_scope.Escape(result);
}
void V8Instance::js_get_role_list(uint32_t index, const PropertyCallbackInfo<Value> &info) {
void *self_v = info.Data().As<External>()->Value();
if (!self_v) {
Logger::write("[v8] [js_get_role_list] Class pointer empty", Logger::LogLevel::Warning);
return;
}
V8Instance *self = static_cast<V8Instance *>(self_v);
void *role_list_v = info.Holder()->GetInternalField(0).As<External>()->Value();
if (!role_list_v) {
Logger::write("[v8] [js_get_role_list] Role List pointer empty", Logger::LogLevel::Warning);
return;
}
std::vector<DiscordObjects::Role *> *role_list = static_cast<std::vector<DiscordObjects::Role *> *>(role_list_v);
if (index < (*role_list).size()) {
Local<Object> role_obj = self->wrap_role((*role_list)[index]);
info.GetReturnValue().Set(role_obj);
}
else {
info.GetReturnValue().SetUndefined();
}
}
/* global functions */
void V8Instance::js_print(const v8::FunctionCallbackInfo<v8::Value> &args) {
auto data = args.Data().As<External>();
V8Instance *self = static_cast<V8Instance *>(data->Value());
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), self->current_input.c_str(), NewStringType::kNormal).ToLocalChecked());
std::string output = "";
for (int i = 0; i < args.Length(); i++) {
v8::String::Utf8Value str(args[i]);
self->print_text += *str;
}
self->print_text += "\n";
}
void V8Instance::js_random(const v8::FunctionCallbackInfo<v8::Value> &args) {
auto data = args.Data().As<External>();
V8Instance *self = static_cast<V8Instance *>(data->Value());
int number_args = args.Length();
if (number_args == 0) {
std::uniform_real_distribution<double> dist(0, 1);
double random_val = dist(self->rng);
args.GetReturnValue().Set(Number::New(args.GetIsolate(), random_val));
}
else if (number_args == 1) {
int64_t max = args[0]->IntegerValue();
std::uniform_int_distribution<int> dist(0, max);
int random_val = dist(self->rng);
args.GetReturnValue().Set(Integer::New(args.GetIsolate(), random_val));
}
else if (number_args == 2) {
int64_t min = args[0]->IntegerValue();
int64_t max = args[1]->IntegerValue();
std::uniform_int_distribution<int> dist(min, max);
int random_val = dist(self->rng);
args.GetReturnValue().Set(Integer::New(args.GetIsolate(), random_val));
}
else {
std::string err_msg = "random() requires 0-2 arguments. You gave: " + std::to_string(number_args);
args.GetIsolate()->ThrowException(String::NewFromUtf8(args.GetIsolate(), err_msg.c_str(), NewStringType::kNormal).ToLocalChecked());
}
}
void V8Instance::js_shuffle(const v8::FunctionCallbackInfo<v8::Value> &args) {
auto data = args.Data().As<External>();
V8Instance *self = static_cast<V8Instance *>(data->Value());
if (!args[0]->IsArray()) {
std::string err_msg = "shuffle() requires an array as it's argument. You gave: " + std::string(*String::Utf8Value(args[0]->TypeOf(args.GetIsolate())));
args.GetIsolate()->ThrowException(String::NewFromUtf8(args.GetIsolate(), err_msg.c_str(), NewStringType::kNormal).ToLocalChecked());
}
else {
Local<Array> given_arr = Local<Array>::Cast(args[0]);
const int length = given_arr->Length();
Local<Array> return_arr = Array::New(args.GetIsolate(), length);
std::vector<Local<Value>> cpp_arr;
for (uint32_t i = 0; i < given_arr->Length(); i++) {
cpp_arr.push_back(given_arr->Get(i));
}
std::shuffle(cpp_arr.begin(), cpp_arr.end(), self->rng);
for (uint32_t i = 0; i < given_arr->Length(); i++) {
return_arr->Set(i, cpp_arr[i]);
}
args.GetReturnValue().Set(return_arr);
}
}
void V8Instance::clean_up() {
Logger::write("[v8] Cleaning up", Logger::LogLevel::Debug);
isolate->Exit();
@ -138,13 +689,29 @@ void V8Instance::reload() {
void V8Instance::exec_js(std::string js, DiscordObjects::Channel *channel, DiscordObjects::GuildMember *sender, std::string args) {
HandleScope handle_scope(isolate);
Local<Context> context(isolate->GetCurrentContext());
Local<Context> context = Local<Context>::New(isolate, context_);
Context::Scope context_scope(context);
context->Global()->Set(
String::NewFromUtf8(isolate, "input", NewStringType::kNormal).ToLocalChecked(),
String::NewFromUtf8(isolate, args.c_str(), NewStringType::kNormal).ToLocalChecked()
);
Local<Object> user_obj = wrap_user(sender);
context->Global()->Set(
String::NewFromUtf8(isolate, "user", NewStringType::kNormal).ToLocalChecked(),
user_obj
);
Local<Object> channel_obj = wrap_user(sender);
context->Global()->Set(
String::NewFromUtf8(isolate, "user", NewStringType::kNormal).ToLocalChecked(),
user_obj
);
// TODO: 'message' object here too, although it's fairly pointless
current_sender = sender;
current_channel = channel;
current_input = args;
Logger::write("[v8] Preparing JS: " + js, Logger::LogLevel::Debug);
Logger::write("[v8] Preparing JS (guild " + (*guilds)[guild_id].id + ", channel " + channel->id + ")", Logger::LogLevel::Debug);
Local<String> source = String::NewFromUtf8(isolate, js.c_str(), NewStringType::kNormal).ToLocalChecked();
@ -154,12 +721,13 @@ void V8Instance::exec_js(std::string js, DiscordObjects::Channel *channel, Disco
TryCatch compile_try_catch(isolate);
Local<Script> script;
auto begin = std::chrono::steady_clock::now();
if (!Script::Compile(context, source).ToLocal(&script)) {
String::Utf8Value error(compile_try_catch.Exception());
std::string err_msg = *error;
Logger::write("[v8] Compilation error: " + err_msg, Logger::LogLevel::Debug);
ah->send_message(channel->id, ":warning: **Compilation error:** `" + err_msg + "`");
DiscordAPI::send_message(channel->id, ":warning: **Compilation error:** `" + err_msg + "`");
return;
}
@ -171,155 +739,18 @@ void V8Instance::exec_js(std::string js, DiscordObjects::Channel *channel, Disco
std::string err_msg = *error;
Logger::write("[v8] Runtime error: " + err_msg, Logger::LogLevel::Debug);
ah->send_message(channel->id, ":warning: **Runtime error:** `" + err_msg + "`");
DiscordAPI::send_message(channel->id, ":warning: **Runtime error:** `" + err_msg + "`");
}
Logger::write("[v8] Script compiled and run", Logger::LogLevel::Debug);
auto end = std::chrono::steady_clock::now();
long long time_taken = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
Logger::write("[v8] Script compiled and run in " + std::to_string(time_taken) + "ms", Logger::LogLevel::Debug);
current_sender = nullptr;
current_channel = nullptr;
current_input = "";
if (print_text != "") {
ah->send_message(channel->id, print_text);
DiscordAPI::send_message(channel->id, print_text);
print_text = "";
}
}
void V8Instance::js_print(const v8::FunctionCallbackInfo<v8::Value> &args) {
auto data = args.Data().As<External>();
V8Instance *self = static_cast<V8Instance *>(data->Value());
std::string output = "";
for (int i = 0; i < args.Length(); i++) {
v8::HandleScope handle_scope(args.GetIsolate());
v8::String::Utf8Value str(args[i]);
self->print_text += *str;
}
}
void V8Instance::add_to_obj(Local<Object> &object, std::string field_name, std::string value) {
add_to_obj(object, field_name, value.c_str());
}
void V8Instance::add_to_obj(Local<Object> &object, std::string field_name, const char value[]) {
if (value == "null") {
object->Set(String::NewFromUtf8(isolate, field_name.c_str(), NewStringType::kNormal).ToLocalChecked(), Null(isolate));
return;
}
object->Set(String::NewFromUtf8(isolate, field_name.c_str(), NewStringType::kNormal).ToLocalChecked(),
String::NewFromUtf8(isolate, value, NewStringType::kNormal).ToLocalChecked());
}
void V8Instance::add_to_obj(Local<Object> &object, std::string field_name, int32_t value) {
object->Set(String::NewFromUtf8(isolate, field_name.c_str(), NewStringType::kNormal).ToLocalChecked(),
Integer::New(isolate, value));
}
void V8Instance::add_to_obj(Local<Object> &object, std::string field_name, bool value) {
object->Set(String::NewFromUtf8(isolate, field_name.c_str(), NewStringType::kNormal).ToLocalChecked(),
Boolean::New(isolate, value));
}
void V8Instance::add_to_obj(Local<Object> &object, std::string field_name, Local<Object> value) {
object->Set(String::NewFromUtf8(isolate, field_name.c_str(), NewStringType::kNormal).ToLocalChecked(), value);
}
void V8Instance::add_to_obj(Local<Object> &object, std::string field_name, Local<Array> value) {
object->Set(String::NewFromUtf8(isolate, field_name.c_str(), NewStringType::kNormal).ToLocalChecked(), value);
}
void V8Instance::add_to_obj(Local<Object> &object, DiscordObjects::Guild guild) {
/* Boobot fields */
add_to_obj(object, "Id", guild.id);
add_to_obj(object, "Name", guild.name);
add_to_obj(object, "IconUrl", "https://discordapp.com/api/guilds/" + guild.id + "/icons/" + guild.icon + ".jpg");
Local<Object> owner_obj = Object::New(isolate);
DiscordObjects::GuildMember &owner = guild.members[guild.owner_id];
add_to_obj(owner_obj, owner);
add_to_obj(object, "Owner", owner_obj);
Local<Array> roles_arr = Array::New(isolate, guild.roles.size());
for (uint32_t i = 0; i < guild.roles.size(); i++) {
Local<Object> obj = Object::New(isolate);
DiscordObjects::Role &role = *guild.roles[i];
add_to_obj(obj, role);
roles_arr->Set(i, obj);
}
add_to_obj(object, "Roles", roles_arr);
Local<Array> members_arr = Array::New(isolate, guild.members.size());
int i = 0;
for (auto it : guild.members) {
Local<Object> obj = Object::New(isolate);
DiscordObjects::GuildMember &member = it.second;
add_to_obj(obj, member);
members_arr->Set(i, obj);
i++;
}
add_to_obj(object, "Users", members_arr);
}
void V8Instance::add_to_obj(Local<Object> &object, DiscordObjects::Channel channel) {
/* Boobot fields */
add_to_obj(object, "Id", channel.id);
add_to_obj(object, "Name", channel.name);
add_to_obj(object, "Topic", channel.topic);
add_to_obj(object, "IsVoice", channel.type == "voice");
Local<Array> users = Array::New(isolate, 1);
users->Set(0, String::NewFromUtf8(isolate, "NOT IMPLEMENTED", NewStringType::kNormal).ToLocalChecked());
add_to_obj(object, "Users", users);
/* Additional fields */
add_to_obj(object, "LastMessageId", channel.last_message_id);
add_to_obj(object, "Bitrate", channel.bitrate);
add_to_obj(object, "UserLimit", channel.user_limit);
}
void V8Instance::add_to_obj(Local<Object> &object, DiscordObjects::Role role) {
/* Boobot fields */
add_to_obj(object, "Id", role.id);
add_to_obj(object, "Name", role.name);
add_to_obj(object, "Position", role.position);
add_to_obj(object, "Red", "NOT IMPLEMENTED");
add_to_obj(object, "Blue", "NOT IMPLEMENTED");
add_to_obj(object, "Green", "NOT IMPLEMENTED");
/* Additional fields */
add_to_obj(object, "Mentionable", role.mentionable);
add_to_obj(object, "Mention", "<@&" + role.id + ">");
add_to_obj(object, "Hoist", role.hoist);
}
void V8Instance::add_to_obj(Local<Object> &object, DiscordObjects::GuildMember member) {
/* Boobot fields */
add_to_obj(object, "Id", member.user->id);
add_to_obj(object, "Name", member.user->username);
add_to_obj(object, "Mention", "<@!" + member.user->id + ">");
add_to_obj(object, "AvatarUrl", "https://discordapp.com/api/users/" + member.user->id + "/avatars/" + member.user->avatar + ".jpg");
Local<Array> roles = Array::New(isolate, member.roles.size());
int i = 0;
for (DiscordObjects::Role *role : member.roles) {
Local<Object> role_obj = Object::New(isolate);
add_to_obj(role_obj, *role);
roles->Set(i, role_obj);
i++;
}
add_to_obj(object, "Roles", roles);
add_to_obj(object, "State", "NOT IMPLEMENTED");
add_to_obj(object, "CurrentGame", "NOT IMPLEMENTED");
/* Additional fields */
add_to_obj(object, "Nick", member.nick);
add_to_obj(object, "Deaf", member.deaf);
add_to_obj(object, "Mute", member.mute);
add_to_obj(object, "JoinedAt", member.joined_at);
}

View File

@ -3,6 +3,7 @@
#include <memory>
#include <map>
#include <random>
#include <include/v8.h>
#include <include/libplatform/libplatform.h>
@ -19,7 +20,7 @@ using namespace v8;
class V8Instance {
public:
V8Instance(std::string guild_id, std::shared_ptr<APIHelper> ah, std::map<std::string, DiscordObjects::Guild> *guilds,
V8Instance(std::string guild_id, std::map<std::string, DiscordObjects::Guild> *guilds,
std::map<std::string, DiscordObjects::Channel> *channels, std::map<std::string, DiscordObjects::User> *users, std::map<std::string, DiscordObjects::Role> *roles);
~V8Instance();
void reload();
@ -30,23 +31,54 @@ private:
void create();
Local<Context> create_context();
void add_to_obj(Local<Object> &object, std::string field_name, std::string value);
void add_to_obj(Local<Object> &object, std::string field_name, const char value[]);
void add_to_obj(Local<Object> &object, std::string field_name, int32_t value);
void add_to_obj(Local<Object> &object, std::string field_name, bool value);
void add_to_obj(Local<Object> &object, std::string field_name, Local<Object> value);
void add_to_obj(Local<Object> &object, std::string field_name, Local<Array> value);
void initialise(Local<Context> context);
void add_to_obj(Local<Object> &object, DiscordObjects::Guild guild);
void add_to_obj(Local<Object> &object, DiscordObjects::Channel channel);
void add_to_obj(Local<Object> &object, DiscordObjects::Role role);
void add_to_obj(Local<Object> &object, DiscordObjects::GuildMember member);
/* server */
Global<ObjectTemplate> server_template;
Local<ObjectTemplate> make_server_template();
Local<Object> wrap_server(DiscordObjects::Guild *guild);
static void js_get_server(Local<Name> property, const PropertyCallbackInfo<Value> &info);
/* user */
Global<ObjectTemplate> user_template;
Local<ObjectTemplate> make_user_template();
Local<Object> wrap_user(DiscordObjects::GuildMember *member);
static void js_get_user(Local<Name> property, const PropertyCallbackInfo<Value> &info);
Global<ObjectTemplate> user_list_template;
Local<ObjectTemplate> make_user_list_template();
Local<Object> wrap_user_list(std::vector<DiscordObjects::GuildMember *> *user_list);
static void js_get_user_list(uint32_t index, const PropertyCallbackInfo<Value> &info);
/* channel */
Global<ObjectTemplate> channel_template;
Local<ObjectTemplate> make_channel_template();
Local<Object> wrap_channel(DiscordObjects::Channel *channel);
static void js_get_channel(Local<Name> property, const PropertyCallbackInfo<Value> &info);
Global<ObjectTemplate> channel_list_template;
Local<ObjectTemplate> make_channel_list_template();
Local<Object> wrap_channel_list(std::vector<DiscordObjects::Channel *> *channel_list);
static void js_get_channel_list(uint32_t index, const PropertyCallbackInfo<Value> &info);
/* role */
Global<ObjectTemplate> role_template;
Local<ObjectTemplate> make_role_template();
Local<Object> wrap_role(DiscordObjects::Role *role);
static void js_get_role(Local<Name> property, const PropertyCallbackInfo<Value> &info);
Global<ObjectTemplate> role_list_template;
Local<ObjectTemplate> make_role_list_template();
Local<Object> wrap_role_list(std::vector<DiscordObjects::Role *> *role_list);
static void js_get_role_list(uint32_t index, const PropertyCallbackInfo<Value> &info);
/* print function */
static void js_print(const FunctionCallbackInfo<Value> &args);
static void js_get_server(Local<String> property, const PropertyCallbackInfo<Value> &info);
static void js_get_channel(Local<String> property, const PropertyCallbackInfo<Value> &info);
static void js_get_user(Local<String> property, const PropertyCallbackInfo<Value> &info);
static void js_get_input(Local<String> property, const PropertyCallbackInfo<Value> &info);
/* randomness functions */
static void js_random(const FunctionCallbackInfo<Value> &args);
static void js_shuffle(const FunctionCallbackInfo<Value> &args);
std::map<std::string, DiscordObjects::Guild> *guilds;
std::map<std::string, DiscordObjects::Channel> *channels;
@ -55,11 +87,14 @@ private:
std::string guild_id;
Isolate *isolate;
std::shared_ptr<APIHelper> ah;
Global<Context> context_;
/* random generating variables */
std::mt19937 rng;
/* variables which change when a new command is executed */
std::string print_text;
std::string current_input;
DiscordObjects::Channel *current_channel;
DiscordObjects::GuildMember *current_sender;
};