Basic JS implementation

This commit is contained in:
Jack Bond-Preston 2016-08-03 22:04:05 +01:00
parent d49c502869
commit 005dad899b
7 changed files with 165 additions and 13 deletions

View File

@ -10,7 +10,7 @@ extern std::string bot_token;
GatewayHandler::GatewayHandler() { GatewayHandler::GatewayHandler() {
last_seq = 0; last_seq = 0;
ah = new APIHelper(); ah = std::make_shared<APIHelper>();
} }
void GatewayHandler::handle_data(std::string data, client &c, websocketpp::connection_hdl &hdl) { void GatewayHandler::handle_data(std::string data, client &c, websocketpp::connection_hdl &hdl) {
@ -68,14 +68,7 @@ void GatewayHandler::on_dispatch(json decoded, client &c, websocketpp::connectio
} }
else if (event_name == "GUILD_CREATE") { else if (event_name == "GUILD_CREATE") {
std::string guild_id = data["id"]; std::string guild_id = data["id"];
try { guilds[guild_id] = std::make_unique<DiscordObjects::Guild>(data);
guilds[guild_id] = std::make_unique<DiscordObjects::Guild>(data);
}
catch (std::domain_error err) {
// this doesn't even work
c.get_alog().write(websocketpp::log::elevel::rerror, "Domain error");
}
c.get_alog().write(websocketpp::log::alevel::app, "Loaded guild: " + guilds[guild_id]->name); c.get_alog().write(websocketpp::log::alevel::app, "Loaded guild: " + guilds[guild_id]->name);
@ -87,6 +80,11 @@ void GatewayHandler::on_dispatch(json decoded, client &c, websocketpp::connectio
// add ptr to said channel list to guild's channel list // add ptr to said channel list to guild's channel list
guilds[guild_id]->channels.push_back(std::shared_ptr<DiscordObjects::Channel>(channels[channel_id])); guilds[guild_id]->channels.push_back(std::shared_ptr<DiscordObjects::Channel>(channels[channel_id]));
} }
if (v8_instances.count(guild_id) == 0) {
v8_instances[guild_id] = std::make_unique<V8Instance>(ah);
c.get_alog().write(websocketpp::log::alevel::app, "Created v8 instance for guild " + guild_id);
}
} }
else if (event_name == "TYPING_START") {} else if (event_name == "TYPING_START") {}
else if (event_name == "MESSAGE_CREATE") { else if (event_name == "MESSAGE_CREATE") {
@ -156,6 +154,13 @@ void GatewayHandler::on_dispatch(json decoded, client &c, websocketpp::connectio
else if (words[0] == "`info") { else if (words[0] == "`info") {
ah->send_message(channel->id, ":information_source: trivia-bot by Jack. <http://github.com/jackb-p/TriviaDiscord>"); ah->send_message(channel->id, ":information_source: trivia-bot by Jack. <http://github.com/jackb-p/TriviaDiscord>");
} }
else if (words[0] == "`js") {
std::string js = message.erase(0, 3);
auto it = v8_instances.find(channel->guild_id);
if (it != v8_instances.end()) {
it->second->exec_js(js, channel->id);
}
}
else if (games.find(channel->id) != games.end()) { // message received in channel with ongoing game else if (games.find(channel->id) != games.end()) { // message received in channel with ongoing game
games[channel->id]->handle_answer(message, sender); games[channel->id]->handle_answer(message, sender);
} }

View File

@ -9,6 +9,7 @@
#include "json/json.hpp" #include "json/json.hpp"
#include "TriviaGame.hpp" #include "TriviaGame.hpp"
#include "js/V8Instance.hpp"
#include "data_structures/User.hpp" #include "data_structures/User.hpp"
#include "data_structures/Guild.hpp" #include "data_structures/Guild.hpp"
#include "data_structures/Channel.hpp" #include "data_structures/Channel.hpp"
@ -68,10 +69,12 @@ private:
// <channel_id, game obj> // <channel_id, game obj>
std::map<std::string, std::unique_ptr<TriviaGame>> games; std::map<std::string, std::unique_ptr<TriviaGame>> games;
// <guild_id, v8 instance>
std::map<std::string, std::unique_ptr<V8Instance>> v8_instances;
std::unique_ptr<boost::thread> heartbeat_thread; std::unique_ptr<boost::thread> heartbeat_thread;
APIHelper *ah; std::shared_ptr<APIHelper> ah;
}; };
#endif #endif

View File

@ -1,4 +1,6 @@
#include <curl/curl.h> #include <curl/curl.h>
#include <include/libplatform/libplatform.h>
#include <include/v8.h>
#include "ClientConnection.hpp" #include "ClientConnection.hpp"
@ -7,6 +9,12 @@ std::string bot_token;
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
curl_global_init(CURL_GLOBAL_DEFAULT); curl_global_init(CURL_GLOBAL_DEFAULT);
v8::V8::InitializeICUDefaultLocation(argv[0]);
v8::V8::InitializeExternalStartupData(argv[0]);
v8::Platform* platform = v8::platform::CreateDefaultPlatform();
v8::V8::InitializePlatform(platform);
v8::V8::Initialize();
if (argc == 2) { if (argc == 2) {
bot_token = argv[1]; bot_token = argv[1];
} }
@ -34,6 +42,10 @@ int main(int argc, char *argv[]) {
std::getchar(); std::getchar();
v8::V8::Dispose();
v8::V8::ShutdownPlatform();
delete platform;
curl_global_cleanup(); curl_global_cleanup();
return 0; return 0;

View File

@ -13,7 +13,7 @@
#include "APIHelper.hpp" #include "APIHelper.hpp"
#include "data_structures/User.hpp" #include "data_structures/User.hpp"
TriviaGame::TriviaGame(GatewayHandler *gh, APIHelper *ah, std::string channel_id, int total_questions, int delay) : interval(delay) { TriviaGame::TriviaGame(GatewayHandler *gh, std::shared_ptr<APIHelper> ah, std::string channel_id, int total_questions, int delay) : interval(delay) {
this->gh = gh; this->gh = gh;
this->ah = ah; this->ah = ah;
this->channel_id = channel_id; this->channel_id = channel_id;

View File

@ -19,7 +19,7 @@ namespace DiscordObjects {
class TriviaGame { class TriviaGame {
public: public:
TriviaGame(GatewayHandler *gh, APIHelper *ah, std::string channel_id, int total_questions, int delay); TriviaGame(GatewayHandler *gh, std::shared_ptr<APIHelper> ah, std::string channel_id, int total_questions, int delay);
~TriviaGame(); ~TriviaGame();
void start(); void start();
@ -38,7 +38,7 @@ private:
std::string channel_id; std::string channel_id;
GatewayHandler *gh; GatewayHandler *gh;
APIHelper *ah; std::shared_ptr<APIHelper> ah;
const char hide_char = '#'; const char hide_char = '#';

View File

@ -0,0 +1,101 @@
#include <iostream>
#include <string>
#include "V8Instance.hpp"
#include "../APIHelper.hpp"
using namespace v8;
V8Instance::V8Instance(std::shared_ptr<APIHelper> ah) {
this->ah = ah;
create();
}
V8Instance::~V8Instance() {
clean_up();
}
void V8Instance::create() {
Isolate::CreateParams create_params;
create_params.array_buffer_allocator = ArrayBuffer::Allocator::NewDefaultAllocator();
isolate = Isolate::New(create_params);
isolate->Enter();
std::cout << "Created isolate." << std::endl;
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
// set global context
Local<Context> context = create_context();
context->Enter();
Context::Scope context_scope(context);
std::cout << "Created context and context scope." << std::endl;
}
v8::Local<v8::Context> V8Instance::create_context() {
Local<ObjectTemplate> global = ObjectTemplate::New(isolate);
// bind print() function
Local<External> self = External::New(isolate, (void *) this);
global->Set(String::NewFromUtf8(isolate, "print", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, V8Instance::js_print, self));
std::cout << "Created global, assigned print function." << std::endl;
return Context::New(isolate, NULL, global);
}
void V8Instance::clean_up() {
std::cout << "Cleaning up." << std::endl;
isolate->Exit();
isolate->Dispose();
delete array_buffer_allocator;
}
void V8Instance::reload() {
clean_up();
create();
}
void V8Instance::exec_js(std::string js, std::string channel_id) {
std::cout << "Isolate nullptr? " << (isolate == nullptr) << std::endl;
HandleScope handle_scope(isolate);
Local<Context> context(isolate->GetCurrentContext());
std::cout << "Executing js: " << js << std::endl;
Local<String> source = String::NewFromUtf8(isolate, js.c_str(), NewStringType::kNormal).ToLocalChecked();
std::cout << "String coverted" << std::endl;
// compile
std::cout << "Context empty? " << context.IsEmpty() << std::endl;
TryCatch try_catch(isolate);
Local<Script> script;
if (!Script::Compile(context, source).ToLocal(&script)) {
String::Utf8Value error(try_catch.Exception());
std::cerr << "Error: " << *error << std::endl;
return;
}
std::cout << "Compiled" << std::endl;
// run
script->Run(context);
std::cout << "Ran" << std::endl;
ah->send_message(channel_id, print_text);
print_text = "";
}
void V8Instance::js_print(const v8::FunctionCallbackInfo<v8::Value> &args) {
auto data = args.Data().As<v8::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;
}
}

View File

@ -0,0 +1,31 @@
#ifndef BOT_JS_V8INSTANCE
#define BOT_JS_V8INSTANCE
#include <memory>
#include <include/v8.h>
#include <include/libplatform/libplatform.h>
class APIHelper;
class V8Instance {
public:
V8Instance(std::shared_ptr<APIHelper> ah);
~V8Instance();
void reload();
void exec_js(std::string js, std::string channel_id);
private:
void clean_up();
void create();
v8::Local<v8::Context> create_context();
static void js_print(const v8::FunctionCallbackInfo<v8::Value> &args);
v8::ArrayBuffer::Allocator *array_buffer_allocator;
v8::Isolate *isolate;
std::shared_ptr<APIHelper> ah;
std::string print_text;
};
#endif