Overhaul a lot, fix the JS implementation
This commit is contained in:
parent
f47d8adc36
commit
e4cc023055
@ -1,59 +0,0 @@
|
||||
#include "APIHelper.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "http/HTTPHelper.hpp"
|
||||
#include "Logger.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
APIHelper::APIHelper() : BASE_URL("https://discordapp.com/api"), CHANNELS_URL(BASE_URL + "/channels"),
|
||||
JSON_CTYPE("application/json") {
|
||||
http = new HTTPHelper();
|
||||
}
|
||||
|
||||
void APIHelper::send_message(std::string channel_id, std::string message) {
|
||||
if (message == "") {
|
||||
Logger::write("[send_message] Tried to send empty message", Logger::LogLevel::Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.length() > 4000) {
|
||||
Logger::write("[send_message] Tried to send a message over 4000 characters", Logger::LogLevel::Warning);
|
||||
return;
|
||||
}
|
||||
else if (message.length() > 2000) {
|
||||
std::cout << message.length() << std::endl;
|
||||
|
||||
std::string first = message.substr(0, 2000);
|
||||
std::string second = message.substr(2000);
|
||||
send_message(channel_id, first);
|
||||
std::this_thread::sleep_for(50ms);
|
||||
send_message(channel_id, second);
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string url = CHANNELS_URL + "/" + channel_id + "/messages";
|
||||
json data = {
|
||||
{ "content", message }
|
||||
};
|
||||
|
||||
std::string response;
|
||||
long response_code = -1;
|
||||
response = http->post_request(url, JSON_CTYPE, data.dump(), &response_code);
|
||||
|
||||
int retries = 0;
|
||||
while (response_code != 200 && retries < 2) {
|
||||
Logger::write("[send_message] Got non-200 response code, retrying", Logger::LogLevel::Warning);
|
||||
std::this_thread::sleep_for(100ms);
|
||||
// try 3 times. usually enough to prevent 502 bad gateway issues
|
||||
response = http->post_request(url, JSON_CTYPE, data.dump(), &response_code);
|
||||
retries++;
|
||||
}
|
||||
|
||||
if (response_code != 200) {
|
||||
Logger::write("[send_message] Giving up on sending message", Logger::LogLevel::Warning);
|
||||
}
|
||||
}
|
84
TriviaBot/bot/DiscordAPI.cpp
Normal file
84
TriviaBot/bot/DiscordAPI.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
#include "DiscordAPI.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "http/HTTPHelper.hpp"
|
||||
#include "Logger.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace DiscordAPI {
|
||||
const std::string base_url = "https://discordapp.com/api";
|
||||
const std::string channels_url = base_url + "/channels";
|
||||
const std::string gateway_url = base_url + "/gateway";
|
||||
|
||||
const std::string json_mime_type = "application/json";
|
||||
|
||||
void send_message(std::string channel_id, std::string message) {
|
||||
if (message == "") {
|
||||
Logger::write("[API] [send_message] Tried to send empty message", Logger::LogLevel::Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.length() > 4000) {
|
||||
Logger::write("[API] [send_message] Tried to send a message over 4000 characters", Logger::LogLevel::Warning);
|
||||
return;
|
||||
}
|
||||
else if (message.length() > 2000) {
|
||||
std::cout << message.length() << std::endl;
|
||||
|
||||
std::string first = message.substr(0, 2000);
|
||||
std::string second = message.substr(2000);
|
||||
send_message(channel_id, first);
|
||||
std::this_thread::sleep_for(50ms);
|
||||
send_message(channel_id, second);
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string url = channels_url + "/" + channel_id + "/messages";
|
||||
json data = {
|
||||
{ "content", message }
|
||||
};
|
||||
|
||||
std::string response;
|
||||
long response_code = 0;
|
||||
response = HTTP::post_request(url, json_mime_type, data.dump(), &response_code);
|
||||
|
||||
int retries = 0;
|
||||
while (response_code != 200 && retries < 2) {
|
||||
Logger::write("[API] [send_message] Got non-200 response code, retrying", Logger::LogLevel::Warning);
|
||||
std::this_thread::sleep_for(100ms);
|
||||
// try 3 times. usually enough to prevent 502 bad gateway issues
|
||||
response = HTTP::post_request(url, json_mime_type, data.dump(), &response_code);
|
||||
retries++;
|
||||
}
|
||||
|
||||
if (response_code != 200) {
|
||||
Logger::write("[API] [send_message] Giving up on sending message", Logger::LogLevel::Warning);
|
||||
}
|
||||
}
|
||||
|
||||
json get_gateway() {
|
||||
std::string response;
|
||||
long response_code;
|
||||
response = HTTP::get_request(gateway_url, &response_code);
|
||||
|
||||
int retries = 0;
|
||||
while (response_code != 200 && retries < 4) {
|
||||
Logger::write("[API] [get_gateway] Got non-200 response code, retrying", Logger::LogLevel::Warning);
|
||||
std::this_thread::sleep_for(100ms);
|
||||
// try 3 times. usually enough to prevent 502 bad gateway issues
|
||||
response = HTTP::get_request(gateway_url, &response_code);
|
||||
retries++;
|
||||
}
|
||||
|
||||
if (response_code != 200) {
|
||||
Logger::write("[API] [get_gateway] Giving up on getting gateway url", Logger::LogLevel::Warning);
|
||||
return json {};
|
||||
}
|
||||
|
||||
return json::parse(response);
|
||||
}
|
||||
}
|
@ -9,18 +9,9 @@ using json = nlohmann::json;
|
||||
|
||||
class HTTPHelper;
|
||||
|
||||
class APIHelper {
|
||||
public:
|
||||
APIHelper();
|
||||
|
||||
namespace DiscordAPI {
|
||||
json get_gateway();
|
||||
void send_message(std::string channel_id, std::string message);
|
||||
|
||||
private:
|
||||
const std::string BASE_URL;
|
||||
const std::string CHANNELS_URL;
|
||||
const std::string JSON_CTYPE;
|
||||
|
||||
HTTPHelper *http;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "APIHelper.hpp"
|
||||
#include "DiscordAPI.hpp"
|
||||
#include "Logger.hpp"
|
||||
#include "data_structures/GuildMember.hpp"
|
||||
|
||||
@ -11,8 +11,7 @@ extern std::string bot_token;
|
||||
GatewayHandler::GatewayHandler() {
|
||||
last_seq = 0;
|
||||
|
||||
ah = std::make_shared<APIHelper>();
|
||||
command_helper = std::make_unique<CommandHelper>();
|
||||
CommandHelper::init();
|
||||
}
|
||||
|
||||
void GatewayHandler::handle_data(std::string data, client &c, websocketpp::connection_hdl &hdl) {
|
||||
@ -33,9 +32,17 @@ void GatewayHandler::handle_data(std::string data, client &c, websocketpp::conne
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayHandler::heartbeat(client *c, websocketpp::connection_hdl hdl, int interval) {
|
||||
void GatewayHandler::send_heartbeat(client *c, websocketpp::connection_hdl hdl, int interval) {
|
||||
while (true) {
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(interval));
|
||||
if (!c) {
|
||||
Logger::write("[send_heartbeat] Client pointer is null", Logger::LogLevel::Severe);
|
||||
break;
|
||||
}
|
||||
else if (c->stopped()) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
json heartbeat = {
|
||||
{ "op", 1 },
|
||||
@ -48,14 +55,49 @@ void GatewayHandler::heartbeat(client *c, websocketpp::connection_hdl hdl, int i
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayHandler::send_identify(client &c, websocketpp::connection_hdl &hdl) {
|
||||
json identify = {
|
||||
{ "op", 2 },
|
||||
{ "d", {
|
||||
{ "token", bot_token },
|
||||
{ "properties",{
|
||||
{ "$browser", "Microsoft Windows 10" },
|
||||
{ "$device", "TriviaBot-0.0" },
|
||||
{ "$referrer", "" },
|
||||
{ "$referring_domain", "" }
|
||||
} },
|
||||
{ "compress", false },
|
||||
{ "large_threshold", 250 },
|
||||
{ "shard",{ 0, 1 } }
|
||||
} }
|
||||
};
|
||||
|
||||
c.send(hdl, identify.dump(), websocketpp::frame::opcode::text);
|
||||
Logger::write("Sent identify payload", Logger::LogLevel::Debug);
|
||||
}
|
||||
|
||||
void GatewayHandler::send_request_guild_members(client &c, websocketpp::connection_hdl &hdl, std::string guild_id) {
|
||||
json request_guild_members = {
|
||||
{ "op", 8 },
|
||||
{ "d", {
|
||||
{ "guild_id", guild_id },
|
||||
{ "query", "" },
|
||||
{ "limit", 0 }
|
||||
} }
|
||||
};
|
||||
|
||||
c.send(hdl, request_guild_members.dump(), websocketpp::frame::opcode::text);
|
||||
Logger::write("Requested guild members for " + guild_id, Logger::LogLevel::Debug);
|
||||
}
|
||||
|
||||
void GatewayHandler::on_hello(json decoded, client &c, websocketpp::connection_hdl &hdl) {
|
||||
heartbeat_interval = decoded["d"]["heartbeat_interval"];
|
||||
|
||||
Logger::write("Heartbeat interval: " + std::to_string(heartbeat_interval / 1000.0f) + " seconds", Logger::LogLevel::Debug);
|
||||
|
||||
heartbeat_thread = std::make_unique<boost::thread>(boost::bind(&GatewayHandler::heartbeat, this, &c, hdl, heartbeat_interval));
|
||||
heartbeat_thread = std::make_unique<boost::thread>(boost::bind(&GatewayHandler::send_heartbeat, this, &c, hdl, heartbeat_interval));
|
||||
|
||||
identify(c, hdl);
|
||||
send_identify(c, hdl);
|
||||
}
|
||||
|
||||
void GatewayHandler::on_dispatch(json decoded, client &c, websocketpp::connection_hdl &hdl) {
|
||||
@ -105,6 +147,9 @@ void GatewayHandler::on_dispatch(json decoded, client &c, websocketpp::connectio
|
||||
else if (event_name == "MESSAGE_CREATE") {
|
||||
on_event_message_create(data);
|
||||
}
|
||||
else if (event_name == "PRESENCE_UPDATE") {
|
||||
on_event_presence_update(data);
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayHandler::on_event_ready(json data) {
|
||||
@ -113,13 +158,31 @@ void GatewayHandler::on_event_ready(json data) {
|
||||
Logger::write("Sign-on confirmed. (@" + user_object.username + "#" + user_object.discriminator + ")", Logger::LogLevel::Info);
|
||||
}
|
||||
|
||||
void GatewayHandler::on_event_presence_update(json data) {
|
||||
std::string user_id = data["user"]["id"];
|
||||
|
||||
auto it = users.find(user_id);
|
||||
if (it != users.end()) {
|
||||
it->second.status = data.value("status", "offline");
|
||||
if (data["game"] == nullptr) {
|
||||
it->second.game = "null";
|
||||
}
|
||||
else {
|
||||
it->second.game = data["game"].value("name", "null");
|
||||
}
|
||||
}
|
||||
else {
|
||||
Logger::write("Tried to add presence for user " + user_id + " who doesn't exist", Logger::LogLevel::Warning);
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayHandler::on_event_guild_create(json data) {
|
||||
guilds[data["id"]] = DiscordObjects::Guild(data);
|
||||
DiscordObjects::Guild &guild = guilds[data["id"]];
|
||||
|
||||
Logger::write("Loaded guild " + guild.id + ", now in " + std::to_string(guilds.size()) + " guild(s)", Logger::LogLevel::Info);
|
||||
Logger::write("Received info for guild " + guild.id + ", now in " + std::to_string(guilds.size()) + " guild(s)", Logger::LogLevel::Info);
|
||||
|
||||
int channels_added = 0, roles_added = 0, members_added = 0;
|
||||
int channels_added = 0, roles_added = 0, members_added = 0, presences_added = 0;
|
||||
|
||||
for (json channel : data["channels"]) {
|
||||
std::string channel_id = channel["id"];
|
||||
@ -147,21 +210,41 @@ void GatewayHandler::on_event_guild_create(json data) {
|
||||
}
|
||||
users[user_id].guilds.push_back(guild.id);
|
||||
|
||||
DiscordObjects::GuildMember guild_member(member, &users[user_id]);
|
||||
DiscordObjects::GuildMember *guild_member = new DiscordObjects::GuildMember(member, &users[user_id]);
|
||||
for (std::string role_id : member["roles"]) {
|
||||
guild_member.roles.push_back(&roles[role_id]);
|
||||
guild_member->roles.push_back(&roles[role_id]);
|
||||
}
|
||||
|
||||
guilds[guild.id].members[user_id] = guild_member;
|
||||
guilds[guild.id].members.push_back(guild_member);
|
||||
|
||||
members_added++;
|
||||
}
|
||||
for (json presence : data["presences"]) {
|
||||
std::string user_id = presence["user"]["id"];
|
||||
|
||||
auto it = users.find(user_id);
|
||||
if (it != users.end()) {
|
||||
it->second.status = presence.value("status", "offline");
|
||||
if (presence["game"] == nullptr) {
|
||||
it->second.game = "null";
|
||||
} else {
|
||||
it->second.game = presence["game"].value("name", "null");
|
||||
}
|
||||
|
||||
presences_added++;
|
||||
}
|
||||
else {
|
||||
Logger::write("Tried to add presence for user " + user_id + " who doesn't exist", Logger::LogLevel::Warning);
|
||||
}
|
||||
}
|
||||
|
||||
if (v8_instances.count(guild.id) == 0) {
|
||||
v8_instances[guild.id] = std::make_unique<V8Instance>(guild.id, ah, &guilds, &channels, &users, &roles);
|
||||
v8_instances[guild.id] = std::make_unique<V8Instance>(guild.id, &guilds, &channels, &users, &roles);
|
||||
Logger::write("Created v8 instance for guild " + guild.id, Logger::LogLevel::Debug);
|
||||
}
|
||||
|
||||
Logger::write("Loaded " + std::to_string(channels_added) + " channels, " + std::to_string(roles_added)
|
||||
+ " roles and " + std::to_string(members_added) + " members to guild " + guild.id, Logger::LogLevel::Debug);
|
||||
Logger::write("Loaded " + std::to_string(channels_added) + " channels, " + std::to_string(roles_added) + " roles and "
|
||||
+ std::to_string(members_added) + " members (with " + std::to_string(presences_added) + " presences) to guild " + guild.id, Logger::LogLevel::Debug);
|
||||
}
|
||||
|
||||
void GatewayHandler::on_event_guild_update(json data) {
|
||||
@ -204,37 +287,41 @@ void GatewayHandler::on_event_guild_member_add(json data) {
|
||||
}
|
||||
users[user_id].guilds.push_back(guild_id);
|
||||
|
||||
DiscordObjects::GuildMember guild_member(data, &users[user_id]);
|
||||
DiscordObjects::GuildMember *guild_member = new DiscordObjects::GuildMember(data, &users[user_id]);
|
||||
for (std::string role_id : data["roles"]) {
|
||||
guild_member.roles.push_back(&roles[role_id]);
|
||||
guild_member->roles.push_back(&roles[role_id]);
|
||||
}
|
||||
|
||||
guilds[guild_id].members[user_id] = guild_member;
|
||||
guilds[guild_id].members.push_back(guild_member);
|
||||
|
||||
Logger::write("Added new member " + guild_member.user->id + " to guild " + guild_id, Logger::LogLevel::Debug);
|
||||
Logger::write("Added new member " + guild_member->user->id + " to guild " + guild_id, Logger::LogLevel::Debug);
|
||||
}
|
||||
|
||||
void GatewayHandler::on_event_guild_member_update(json data) {
|
||||
std::string user_id = data["user"]["id"];
|
||||
DiscordObjects::Guild &guild = guilds[data["guild_id"]];
|
||||
|
||||
auto it = guild.members.find(user_id);
|
||||
auto it = std::find_if(guild.members.begin(), guild.members.end(), [user_id](DiscordObjects::GuildMember *member) {
|
||||
return user_id == member->user->id;
|
||||
});
|
||||
if (it != guild.members.end()) {
|
||||
bool nick_changed = false;
|
||||
size_t roles_change = 0;
|
||||
|
||||
DiscordObjects::GuildMember *member = (*it);
|
||||
|
||||
std::string nick = data.value("nick", "null");
|
||||
if (it->second.nick != nick) {
|
||||
it->second.nick = nick;
|
||||
if (member->nick != nick) {
|
||||
member->nick = nick;
|
||||
nick_changed = true;
|
||||
}
|
||||
|
||||
roles_change = it->second.roles.size();
|
||||
it->second.roles.clear(); // reset and re-fill, changing the differences is probably more expensive anyway.
|
||||
roles_change = member->roles.size();
|
||||
member->roles.clear(); // reset and re-fill, changing the differences is probably more expensive anyway.
|
||||
for (std::string role_id : data["roles"]) {
|
||||
it->second.roles.push_back(&roles[role_id]);
|
||||
member->roles.push_back(&roles[role_id]);
|
||||
}
|
||||
roles_change = it->second.roles.size() - roles_change;
|
||||
roles_change = member->roles.size() - roles_change;
|
||||
|
||||
std::string debug_string = "Updated member " + user_id + " of guild " + guild.id;
|
||||
if (nick_changed) debug_string += ". Nick changed to " + nick;
|
||||
@ -252,14 +339,16 @@ void GatewayHandler::on_event_guild_member_remove(json data) {
|
||||
DiscordObjects::Guild &guild = guilds[data["guild_id"]];
|
||||
std::string user_id = data["user"]["id"];
|
||||
|
||||
auto it = guild.members.find(user_id);
|
||||
if (it != guilds[guild.id].members.end()) {
|
||||
auto it = std::find_if(guild.members.begin(), guild.members.end(), [user_id](DiscordObjects::GuildMember *member) {
|
||||
return user_id == member->user->id;
|
||||
});
|
||||
if (it != guild.members.end()) {
|
||||
delete (*it);
|
||||
guild.members.erase(it);
|
||||
|
||||
DiscordObjects::User &user = users[user_id];
|
||||
user.guilds.erase(std::remove(user.guilds.begin(), user.guilds.end(), guild.id));
|
||||
users[user_id].guilds.erase(std::remove(users[user_id].guilds.begin(), users[user_id].guilds.end(), guild.id));
|
||||
|
||||
if (user.guilds.size() == 0) {
|
||||
if (users[user_id].guilds.size() == 0) {
|
||||
users.erase(users.find(user_id));
|
||||
Logger::write("User " + user_id + " removed from guild " + guild.id + " and no longer visible, deleted.", Logger::LogLevel::Debug);
|
||||
}
|
||||
@ -327,8 +416,6 @@ void GatewayHandler::on_event_channel_create(json data) {
|
||||
}
|
||||
|
||||
void GatewayHandler::on_event_channel_update(json data) {
|
||||
std::cout << "Update: " << data.dump(4) << std::endl;
|
||||
|
||||
std::string channel_id = data["id"];
|
||||
|
||||
auto it = channels.find(channel_id);
|
||||
@ -342,8 +429,6 @@ void GatewayHandler::on_event_channel_update(json data) {
|
||||
}
|
||||
|
||||
void GatewayHandler::on_event_channel_delete(json data) {
|
||||
std::cout << "Delete: " << data.dump(4) << std::endl;
|
||||
|
||||
std::string channel_id = data["id"];
|
||||
std::string guild_id = data["guild_id"];
|
||||
|
||||
@ -369,19 +454,19 @@ void GatewayHandler::on_event_message_create(json data) {
|
||||
|
||||
DiscordObjects::Channel &channel = channels[data["channel_id"]];
|
||||
DiscordObjects::Guild &guild = guilds[channel.guild_id];
|
||||
DiscordObjects::GuildMember &sender = guild.members[data["author"]["id"]];
|
||||
DiscordObjects::User &sender = users[data["author"]["id"]];
|
||||
|
||||
if (sender.user->bot) return; // ignore bots to prevent looping
|
||||
if (sender.bot) return;
|
||||
|
||||
std::vector<std::string> words;
|
||||
boost::split(words, message, boost::is_any_of(" "));
|
||||
Command custom_command;
|
||||
CommandHelper::Command custom_command;
|
||||
if (words[0] == "`trivia" || words[0] == "`t") {
|
||||
int questions = 10;
|
||||
int delay = 8;
|
||||
|
||||
if (words.size() > 3) {
|
||||
ah->send_message(channel.id, ":exclamation: Invalid arguments!");
|
||||
DiscordAPI::send_message(channel.id, ":exclamation: Invalid arguments!");
|
||||
return;
|
||||
}
|
||||
else if (words.size() > 1) {
|
||||
@ -391,7 +476,7 @@ void GatewayHandler::on_event_message_create(json data) {
|
||||
help += "\\`trivia **stop**: stops the ongoing game.\n";
|
||||
help += "\\`trivia **help**: prints this message\n";
|
||||
|
||||
ah->send_message(channel.id, help);
|
||||
DiscordAPI::send_message(channel.id, help);
|
||||
return;
|
||||
}
|
||||
else if (words[1] == "stop" || words[1] == "s") {
|
||||
@ -408,13 +493,13 @@ void GatewayHandler::on_event_message_create(json data) {
|
||||
}
|
||||
}
|
||||
catch (std::invalid_argument e) {
|
||||
ah->send_message(channel.id, ":exclamation: Invalid arguments!");
|
||||
DiscordAPI::send_message(channel.id, ":exclamation: Invalid arguments!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
games[channel.id] = std::make_unique<TriviaGame>(this, ah, channel.id, questions, delay);
|
||||
games[channel.id] = std::make_unique<TriviaGame>(this, channel.id, questions, delay);
|
||||
games[channel.id]->start();
|
||||
}
|
||||
else if (words[0] == "`guilds") {
|
||||
@ -422,56 +507,59 @@ void GatewayHandler::on_event_message_create(json data) {
|
||||
for (auto &gu : guilds) {
|
||||
m += "> " + gu.second.name + " (" + gu.second.id + ") Channels: " + std::to_string(gu.second.channels.size()) + "\n";
|
||||
}
|
||||
ah->send_message(channel.id, m);
|
||||
DiscordAPI::send_message(channel.id, m);
|
||||
}
|
||||
else if (words[0] == "`info") {
|
||||
ah->send_message(channel.id, ":information_source: trivia-bot by Jack. <http://github.com/jackb-p/TriviaDiscord>");
|
||||
DiscordAPI::send_message(channel.id, ":information_source: trivia-bot by Jack. <http://github.com/jackb-p/TriviaDiscord>");
|
||||
}
|
||||
else if (words[0] == "~js" && message.length() > 4) {
|
||||
else if (words[0] == "~js" && words.size() > 1) {
|
||||
DiscordObjects::GuildMember *member = *std::find_if(guild.members.begin(), guild.members.end(), [sender](DiscordObjects::GuildMember *m) {
|
||||
return sender.id == m->user->id;
|
||||
});
|
||||
std::string js = message.substr(4);
|
||||
auto it = v8_instances.find(channel.guild_id);
|
||||
if (it != v8_instances.end() && js.length() > 0) {
|
||||
it->second->exec_js(js, &channel, &sender);
|
||||
it->second->exec_js(js, &channel, member);
|
||||
}
|
||||
}
|
||||
else if (words[0] == "~createjs" && message.length() > 8) {
|
||||
else if (words[0] == "~createjs" && words.size() > 1) {
|
||||
std::string args = message.substr(10);
|
||||
size_t seperator_loc = args.find("|");
|
||||
if (seperator_loc != std::string::npos) {
|
||||
std::string command_name = args.substr(0, seperator_loc);
|
||||
std::string script = args.substr(seperator_loc + 1);
|
||||
int result = command_helper->insert_command(channel.guild_id, command_name, script);
|
||||
int result = CommandHelper::insert_command(channel.guild_id, command_name, script);
|
||||
switch (result) {
|
||||
case 0:
|
||||
ah->send_message(channel.id, ":warning: Error!"); break;
|
||||
DiscordAPI::send_message(channel.id, ":warning: Error!"); break;
|
||||
case 1:
|
||||
ah->send_message(channel.id, ":new: Command `" + command_name + "` successfully created."); break;
|
||||
DiscordAPI::send_message(channel.id, ":new: Command `" + command_name + "` successfully created."); break;
|
||||
case 2:
|
||||
ah->send_message(channel.id, ":arrow_heading_up: Command `" + command_name + "` successfully updated."); break;
|
||||
DiscordAPI::send_message(channel.id, ":arrow_heading_up: Command `" + command_name + "` successfully updated."); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (words[0] == "`shutdown" && sender.user->id == "82232146579689472") { // it me
|
||||
ah->send_message(channel.id, ":zzz: Goodbye!");
|
||||
else if (words[0] == "`shutdown" && sender.id == "82232146579689472") { // it me
|
||||
DiscordAPI::send_message(channel.id, ":zzz: Goodbye!");
|
||||
// TODO: without needing c, hdl - c.close(hdl, websocketpp::close::status::going_away, "`shutdown command used.");
|
||||
}
|
||||
else if (words[0] == "`debug") {
|
||||
if (words[1] == "channel" && words.size() == 3) {
|
||||
auto it = channels.find(words[2]);
|
||||
if (it != channels.end()) {
|
||||
ah->send_message(channel.id, it->second.to_debug_string());
|
||||
DiscordAPI::send_message(channel.id, it->second.to_debug_string());
|
||||
}
|
||||
else {
|
||||
ah->send_message(channel.id, ":question: Unrecognised channel.");
|
||||
DiscordAPI::send_message(channel.id, ":question: Unrecognised channel.");
|
||||
}
|
||||
}
|
||||
else if (words[1] == "guild" && words.size() == 3) {
|
||||
auto it = guilds.find(words[2]);
|
||||
if (it != guilds.end()) {
|
||||
ah->send_message(channel.id, it->second.to_debug_string());
|
||||
DiscordAPI::send_message(channel.id, it->second.to_debug_string());
|
||||
}
|
||||
else {
|
||||
ah->send_message(channel.id, ":question: Unrecognised guild.");
|
||||
DiscordAPI::send_message(channel.id, ":question: Unrecognised guild.");
|
||||
}
|
||||
}
|
||||
else if (words[1] == "member" && words.size() == 4) {
|
||||
@ -479,25 +567,27 @@ void GatewayHandler::on_event_message_create(json data) {
|
||||
if (it != guilds.end()) {
|
||||
std::string user_id = words[3];
|
||||
|
||||
auto it2 = it->second.members.find(user_id);
|
||||
auto it2 = std::find_if(it->second.members.begin(), it->second.members.end(), [user_id](DiscordObjects::GuildMember *member) {
|
||||
return user_id == member->user->id;
|
||||
});
|
||||
if (it2 != it->second.members.end()) {
|
||||
ah->send_message(channel.id, it2->second.to_debug_string());
|
||||
DiscordAPI::send_message(channel.id, (*it2)->to_debug_string());
|
||||
}
|
||||
else {
|
||||
ah->send_message(channel.id, ":question: Unrecognised user.");
|
||||
DiscordAPI::send_message(channel.id, ":question: Unrecognised user.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
ah->send_message(channel.id, ":question: Unrecognised guild.");
|
||||
DiscordAPI::send_message(channel.id, ":question: Unrecognised guild.");
|
||||
}
|
||||
}
|
||||
else if (words[1] == "role" && words.size() == 3) {
|
||||
auto it = roles.find(words[2]);
|
||||
if (it != roles.end()) {
|
||||
ah->send_message(channel.id, it->second.to_debug_string());
|
||||
DiscordAPI::send_message(channel.id, it->second.to_debug_string());
|
||||
}
|
||||
else {
|
||||
ah->send_message(channel.id, ":question: Unrecognised role.");
|
||||
DiscordAPI::send_message(channel.id, ":question: Unrecognised role.");
|
||||
}
|
||||
}
|
||||
else if (words[1] == "role" && words.size() == 4) {
|
||||
@ -511,21 +601,21 @@ void GatewayHandler::on_event_message_create(json data) {
|
||||
|
||||
auto it2 = std::find_if(it->second.roles.begin(), it->second.roles.end(), check_lambda);
|
||||
if (it2 != it->second.roles.end()) {
|
||||
ah->send_message(channel.id, (*it2)->to_debug_string());
|
||||
DiscordAPI::send_message(channel.id, (*it2)->to_debug_string());
|
||||
}
|
||||
else {
|
||||
ah->send_message(channel.id, ":question: Unrecognised role.");
|
||||
DiscordAPI::send_message(channel.id, ":question: Unrecognised role.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
ah->send_message(channel.id, ":question: Unrecognised guild.");
|
||||
DiscordAPI::send_message(channel.id, ":question: Unrecognised guild.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
ah->send_message(channel.id, ":question: Unknown parameters.");
|
||||
DiscordAPI::send_message(channel.id, ":question: Unknown parameters.");
|
||||
}
|
||||
}
|
||||
else if (command_helper->get_command(channel.guild_id, words[0], custom_command)) {
|
||||
else if (CommandHelper::get_command(channel.guild_id, words[0], custom_command)) {
|
||||
std::string args = "";
|
||||
if (message.length() > (words[0].length() + 1)) {
|
||||
args = message.substr(words[0].length() + 1);
|
||||
@ -533,35 +623,17 @@ void GatewayHandler::on_event_message_create(json data) {
|
||||
|
||||
auto it = v8_instances.find(channel.guild_id);
|
||||
if (it != v8_instances.end() && custom_command.script.length() > 0) {
|
||||
it->second->exec_js(custom_command.script, &channel, &sender, args);
|
||||
DiscordObjects::GuildMember *member = *std::find_if(guild.members.begin(), guild.members.end(), [sender](DiscordObjects::GuildMember *m) {
|
||||
return sender.id == m->user->id;
|
||||
});
|
||||
it->second->exec_js(custom_command.script, &channel, member, args);
|
||||
}
|
||||
}
|
||||
else if (games.find(channel.id) != games.end()) { // message received in channel with ongoing game
|
||||
games[channel.id]->handle_answer(message, *sender.user);
|
||||
games[channel.id]->handle_answer(message, sender);
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayHandler::identify(client &c, websocketpp::connection_hdl &hdl) {
|
||||
json identify = {
|
||||
{ "op", 2 },
|
||||
{ "d", {
|
||||
{ "token", bot_token },
|
||||
{ "properties", {
|
||||
{ "$browser", "Microsoft Windows 10" },
|
||||
{ "$device", "TriviaBot-0.0" },
|
||||
{ "$referrer", "" },
|
||||
{ "$referring_domain", "" }
|
||||
} },
|
||||
{ "compress", false },
|
||||
{ "large_threshold", 250 },
|
||||
{ "shard", { 0, 1 } }
|
||||
} }
|
||||
};
|
||||
|
||||
c.send(hdl, identify.dump(), websocketpp::frame::opcode::text);
|
||||
Logger::write("Sent identify payload", Logger::LogLevel::Debug);
|
||||
}
|
||||
|
||||
void GatewayHandler::delete_game(std::string channel_id) {
|
||||
auto it = games.find(channel_id);
|
||||
|
||||
|
@ -45,21 +45,24 @@ public:
|
||||
|
||||
void handle_data(std::string data, client &c, websocketpp::connection_hdl &hdl);
|
||||
|
||||
void heartbeat(client *c, websocketpp::connection_hdl hdl, int interval);
|
||||
|
||||
void on_hello(json decoded, client &c, websocketpp::connection_hdl &hdl);
|
||||
|
||||
void on_dispatch(json decoded, client &c, websocketpp::connection_hdl &hdl);
|
||||
|
||||
void identify(client &c, websocketpp::connection_hdl &hdl);
|
||||
|
||||
void delete_game(std::string channel_id);
|
||||
|
||||
private:
|
||||
int last_seq;
|
||||
int heartbeat_interval;
|
||||
|
||||
void on_event_ready(json data);
|
||||
/* payload dispatchers */
|
||||
void send_heartbeat(client *c, websocketpp::connection_hdl hdl, int interval);
|
||||
void send_identify(client &c, websocketpp::connection_hdl &hdl);
|
||||
void send_request_guild_members(client &c, websocketpp::connection_hdl &hdl, std::string guild_id); // not sure if required atm
|
||||
|
||||
/* payload handlers */
|
||||
void on_hello(json decoded, client &c, websocketpp::connection_hdl &hdl);
|
||||
void on_dispatch(json decoded, client &c, websocketpp::connection_hdl &hdl);
|
||||
|
||||
/* misc events */
|
||||
void on_event_ready(json data); // https://discordapp.com/developers/docs/topics/gateway#ready
|
||||
void on_event_presence_update(json data); // https://discordapp.com/developers/docs/topics/gateway#presence-update
|
||||
|
||||
/* guild events */
|
||||
void on_event_guild_create(json data); // https://discordapp.com/developers/docs/topics/gateway#guild-create
|
||||
@ -85,8 +88,6 @@ private:
|
||||
// bot's user obj
|
||||
DiscordObjects::User user_object;
|
||||
|
||||
std::unique_ptr<CommandHelper> command_helper;
|
||||
|
||||
/* <id, obj> */
|
||||
std::map<std::string, DiscordObjects::Guild> guilds;
|
||||
std::map<std::string, DiscordObjects::Channel> channels;
|
||||
@ -99,8 +100,6 @@ private:
|
||||
std::map<std::string, std::unique_ptr<V8Instance>> v8_instances;
|
||||
|
||||
std::unique_ptr<boost::thread> heartbeat_thread;
|
||||
|
||||
std::shared_ptr<APIHelper> ah;
|
||||
};
|
||||
|
||||
#endif
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "ClientConnection.hpp"
|
||||
#include "Logger.hpp"
|
||||
#include "DiscordAPI.hpp"
|
||||
|
||||
std::string bot_token;
|
||||
|
||||
@ -26,21 +27,26 @@ int main(int argc, char *argv[]) {
|
||||
std::cin >> bot_token;
|
||||
}
|
||||
|
||||
// todo: get this using API
|
||||
std::string uri = "wss://gateway.discord.gg/?v=5&encoding=json";
|
||||
std::string args = "/?v=5&encoding=json";
|
||||
std::string url = DiscordAPI::get_gateway().value("url", "wss://gateway.discord.gg");
|
||||
|
||||
try {
|
||||
ClientConnection conn;
|
||||
conn.start(uri);
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
Logger::write("std exception: " + std::string(e.what()), Logger::LogLevel::Severe);
|
||||
}
|
||||
catch (websocketpp::lib::error_code e) {
|
||||
Logger::write("websocketpp exception: " + e.message(), Logger::LogLevel::Severe);
|
||||
}
|
||||
catch (...) {
|
||||
Logger::write("other exception.", Logger::LogLevel::Severe);
|
||||
bool retry = true;
|
||||
while (retry) {
|
||||
try {
|
||||
ClientConnection conn;
|
||||
conn.start(url + args);
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
Logger::write("std exception: " + std::string(e.what()), Logger::LogLevel::Severe);
|
||||
retry = false;
|
||||
}
|
||||
catch (websocketpp::lib::error_code e) {
|
||||
Logger::write("websocketpp exception: " + e.message(), Logger::LogLevel::Severe);
|
||||
}
|
||||
catch (...) {
|
||||
Logger::write("other exception.", Logger::LogLevel::Severe);
|
||||
retry = false;
|
||||
}
|
||||
}
|
||||
|
||||
v8::V8::Dispose();
|
||||
|
@ -10,13 +10,12 @@
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
#include "GatewayHandler.hpp"
|
||||
#include "APIHelper.hpp"
|
||||
#include "DiscordAPI.hpp"
|
||||
#include "data_structures/User.hpp"
|
||||
#include "Logger.hpp"
|
||||
|
||||
TriviaGame::TriviaGame(GatewayHandler *gh, std::shared_ptr<APIHelper> ah, std::string channel_id, int total_questions, int delay) : interval(delay) {
|
||||
TriviaGame::TriviaGame(GatewayHandler *gh, std::string channel_id, int total_questions, int delay) : interval(delay) {
|
||||
this->gh = gh;
|
||||
this->ah = ah;
|
||||
this->channel_id = channel_id;
|
||||
|
||||
this->total_questions = total_questions;
|
||||
@ -27,7 +26,7 @@ TriviaGame::~TriviaGame() {
|
||||
current_thread.reset();
|
||||
|
||||
if (scores.size() == 0) {
|
||||
ah->send_message(channel_id, ":red_circle: Game cancelled!");
|
||||
DiscordAPI::send_message(channel_id, ":red_circle: Game cancelled!");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -51,7 +50,7 @@ TriviaGame::~TriviaGame() {
|
||||
average_time.pop_back(); average_time.pop_back(); average_time.pop_back();
|
||||
message += ":small_blue_diamond: <@!" + p.first + ">: " + std::to_string(p.second) + " (Avg: " + average_time + " seconds)\n";
|
||||
}
|
||||
ah->send_message(channel_id, message);
|
||||
DiscordAPI::send_message(channel_id, message);
|
||||
|
||||
sqlite3 *db; int rc; std::string sql;
|
||||
|
||||
@ -214,7 +213,7 @@ void TriviaGame::question() {
|
||||
sqlite3_close(db);
|
||||
|
||||
questions_asked++;
|
||||
ah->send_message(channel_id, ":question: **(" + std::to_string(questions_asked) + "/" + std::to_string(total_questions) + ")** " + current_question);
|
||||
DiscordAPI::send_message(channel_id, ":question: **(" + std::to_string(questions_asked) + "/" + std::to_string(total_questions) + ")** " + current_question);
|
||||
question_start = boost::posix_time::microsec_clock::universal_time();
|
||||
|
||||
give_hint(0, "");
|
||||
@ -281,11 +280,11 @@ void TriviaGame::give_hint(int hints_given, std::string hint) {
|
||||
hints_given++; // now equal to the amount of [hide_char]s that need to be present in each word
|
||||
|
||||
if (print) {
|
||||
ah->send_message(channel_id, ":small_orange_diamond: Hint: **`" + hint + "`**");
|
||||
DiscordAPI::send_message(channel_id, ":small_orange_diamond: Hint: **`" + hint + "`**");
|
||||
}
|
||||
}
|
||||
boost::this_thread::sleep(interval);
|
||||
ah->send_message(channel_id, ":exclamation: Question failed. Answer: ** `" + *current_answers.begin() + "` **");
|
||||
DiscordAPI::send_message(channel_id, ":exclamation: Question failed. Answer: ** `" + *current_answers.begin() + "` **");
|
||||
}
|
||||
|
||||
void TriviaGame::handle_answer(std::string answer, DiscordObjects::User sender) {
|
||||
@ -300,7 +299,7 @@ void TriviaGame::handle_answer(std::string answer, DiscordObjects::User sender)
|
||||
// remove the last three 0s
|
||||
time_taken.pop_back(); time_taken.pop_back(); time_taken.pop_back();
|
||||
|
||||
ah->send_message(channel_id, ":heavy_check_mark: <@!" + sender.id + "> You got it! (" + time_taken + " seconds)");
|
||||
DiscordAPI::send_message(channel_id, ":heavy_check_mark: <@!" + sender.id + "> You got it! (" + time_taken + " seconds)");
|
||||
|
||||
increase_score(sender.id);
|
||||
update_average_time(sender.id, diff.total_milliseconds());
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
|
||||
class GatewayHandler;
|
||||
class APIHelper;
|
||||
namespace DiscordObjects {
|
||||
class User;
|
||||
}
|
||||
@ -19,7 +18,7 @@ namespace DiscordObjects {
|
||||
|
||||
class TriviaGame {
|
||||
public:
|
||||
TriviaGame(GatewayHandler *gh, std::shared_ptr<APIHelper> ah, std::string channel_id, int total_questions, int delay);
|
||||
TriviaGame(GatewayHandler *gh, std::string channel_id, int total_questions, int delay);
|
||||
~TriviaGame();
|
||||
|
||||
void start();
|
||||
@ -38,7 +37,6 @@ private:
|
||||
|
||||
std::string channel_id;
|
||||
GatewayHandler *gh;
|
||||
std::shared_ptr<APIHelper> ah;
|
||||
|
||||
const char hide_char = '#';
|
||||
|
||||
|
@ -57,7 +57,7 @@ namespace DiscordObjects {
|
||||
type = "text";
|
||||
}
|
||||
|
||||
inline Channel::Channel(json data) {
|
||||
inline Channel::Channel(json data) : Channel() {
|
||||
load_from_json(data);
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ namespace DiscordObjects {
|
||||
bool unavailable;
|
||||
|
||||
std::vector<Channel *> channels;
|
||||
std::map<std::string, GuildMember> members;
|
||||
std::vector<GuildMember *> members;
|
||||
std::vector<Role *> roles;
|
||||
//std::vector<std::unique_ptr<DiscordObjects::User>> users;
|
||||
};
|
||||
@ -83,13 +83,11 @@ namespace DiscordObjects {
|
||||
afk_timeout = verification_level = -1;
|
||||
}
|
||||
|
||||
inline Guild::Guild(json data) {
|
||||
inline Guild::Guild(json data) : Guild() {
|
||||
load_from_json(data);
|
||||
}
|
||||
|
||||
inline void Guild::load_from_json(json data) {
|
||||
Guild();
|
||||
|
||||
id = data.value("id", "null");
|
||||
name = data.value("name", "null");
|
||||
icon = data.value("icon", "null");
|
||||
|
@ -30,13 +30,12 @@ namespace DiscordObjects {
|
||||
|
||||
inline GuildMember::GuildMember() {
|
||||
user = nullptr;
|
||||
nick = "null";
|
||||
joined_at = "null";
|
||||
nick = joined_at = "null";
|
||||
deaf = false;
|
||||
mute = false;
|
||||
}
|
||||
|
||||
inline GuildMember::GuildMember(json data, User *user) {
|
||||
inline GuildMember::GuildMember(json data, User *user) : GuildMember() {
|
||||
this->user = user;
|
||||
load_from_json(data);
|
||||
}
|
||||
@ -54,6 +53,8 @@ namespace DiscordObjects {
|
||||
+ "\n**bot:** " + std::to_string(user->bot)
|
||||
+ "\n**mfa_enabled:** " + std::to_string(user->mfa_enabled)
|
||||
+ "\n**avatar:** " + user->avatar
|
||||
+ "\n**status:** " + user->status
|
||||
+ "\n**game name:** " + user->game
|
||||
+ "\n**nick:** " + nick
|
||||
+ "\n**joined_at:** " + joined_at
|
||||
+ "\n**deaf:** " + std::to_string(deaf)
|
||||
|
@ -42,7 +42,7 @@ namespace DiscordObjects {
|
||||
mentionable = false;
|
||||
}
|
||||
|
||||
inline Role::Role(json data) {
|
||||
inline Role::Role(json data) : Role() {
|
||||
load_from_json(data);
|
||||
}
|
||||
|
||||
|
@ -39,15 +39,20 @@ namespace DiscordObjects {
|
||||
bool bot;
|
||||
bool mfa_enabled;
|
||||
|
||||
// presence
|
||||
std::string game;
|
||||
std::string status;
|
||||
|
||||
std::vector<std::string> guilds;
|
||||
};
|
||||
|
||||
inline User::User() {
|
||||
id = username = discriminator = avatar = "null";
|
||||
id = username = discriminator = avatar = game = "null";
|
||||
status = "offline";
|
||||
bot = mfa_enabled = false;
|
||||
}
|
||||
|
||||
inline User::User(json data) {
|
||||
inline User::User(json data) : User() {
|
||||
load_from_json(data);
|
||||
}
|
||||
|
||||
|
@ -8,52 +8,97 @@ extern std::string bot_token;
|
||||
* Warning: (Awful) C Code
|
||||
*/
|
||||
|
||||
std::string HTTPHelper::post_request(std::string url, std::string content_type, std::string data, long *response_code) {
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
std::string read_buffer;
|
||||
struct curl_slist *headers = nullptr;
|
||||
|
||||
curl = curl_easy_init();
|
||||
if (curl) {
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
|
||||
// Now with real HTTPS!
|
||||
curl_easy_setopt(curl, CURLOPT_CAINFO, "bot/http/DiscordCA.crt");
|
||||
|
||||
std::string header_arr[3];
|
||||
header_arr[0] = "Content-Type: " + content_type;
|
||||
header_arr[1] = "Authorization: Bot " + bot_token;
|
||||
header_arr[2] = "User-Agent: DiscordBot(http://github.com/jackb-p/triviadiscord, 1.0)";
|
||||
|
||||
for (std::string h : header_arr) {
|
||||
headers = curl_slist_append(headers, h.c_str());
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &read_buffer);
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
|
||||
if (res == CURLE_OK) {
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, response_code);
|
||||
} else {
|
||||
Logger::write("curl error", Logger::LogLevel::Warning);
|
||||
return "";
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
curl_slist_free_all(headers);
|
||||
namespace HTTP {
|
||||
size_t write_callback(void *contents, size_t size, size_t nmemb, void *read_buffer) {
|
||||
static_cast<std::string *>(read_buffer)->append(static_cast<char *>(contents), size * nmemb);
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
return read_buffer;
|
||||
}
|
||||
std::string post_request(std::string url, std::string content_type, std::string data, long *response_code) {
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
std::string read_buffer;
|
||||
struct curl_slist *headers = nullptr;
|
||||
|
||||
size_t HTTPHelper::write_callback(void *contents, size_t size, size_t nmemb, void *userp) {
|
||||
((std::string *) userp)->append((char *) contents, size * nmemb);
|
||||
return size * nmemb;
|
||||
curl = curl_easy_init();
|
||||
if (curl) {
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
|
||||
// Now with real HTTPS!
|
||||
curl_easy_setopt(curl, CURLOPT_CAINFO, "bot/http/DiscordCA.crt");
|
||||
|
||||
std::string header_arr[3];
|
||||
header_arr[0] = "Content-Type: " + content_type;
|
||||
header_arr[1] = "Authorization: Bot " + bot_token;
|
||||
header_arr[2] = "User-Agent: DiscordBot(http://github.com/jackb-p/triviadiscord, 1.0)";
|
||||
|
||||
for (std::string h : header_arr) {
|
||||
headers = curl_slist_append(headers, h.c_str());
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &read_buffer);
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
|
||||
if (res == CURLE_OK) {
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, response_code);
|
||||
}
|
||||
else {
|
||||
return "";
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
curl_slist_free_all(headers);
|
||||
}
|
||||
|
||||
return read_buffer;
|
||||
}
|
||||
|
||||
std::string get_request(std::string url, long *response_code) {
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
std::string read_buffer;
|
||||
struct curl_slist *headers = nullptr;
|
||||
|
||||
curl = curl_easy_init();
|
||||
if (curl) {
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
|
||||
// Now with real HTTPS!
|
||||
curl_easy_setopt(curl, CURLOPT_CAINFO, "bot/http/DiscordCA.crt");
|
||||
|
||||
std::string header_arr[2];
|
||||
header_arr[0] = "Authorization: Bot " + bot_token;
|
||||
header_arr[1] = "User-Agent: DiscordBot (http://github.com/jackb-p/triviadiscord, 1.0)";
|
||||
|
||||
for (std::string h : header_arr) {
|
||||
headers = curl_slist_append(headers, h.c_str());
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &HTTP::write_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &read_buffer);
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
|
||||
if (res == CURLE_OK) {
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, response_code);
|
||||
}
|
||||
else {
|
||||
Logger::write("curl error: " + std::string(curl_easy_strerror(res)), Logger::LogLevel::Warning);
|
||||
return read_buffer;
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
curl_slist_free_all(headers);
|
||||
}
|
||||
|
||||
return read_buffer;
|
||||
}
|
||||
}
|
@ -5,12 +5,9 @@
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
class HTTPHelper {
|
||||
public:
|
||||
namespace HTTP {
|
||||
std::string post_request(std::string url, std::string content_type, std::string data, long *response_code);
|
||||
|
||||
private:
|
||||
static size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp);
|
||||
};
|
||||
std::string get_request(std::string url, long *response_code);
|
||||
}
|
||||
|
||||
#endif
|
@ -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;
|
||||
}
|
@ -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
|
@ -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);
|
||||
}
|
@ -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;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user