Mercurial > irccd
view libirccd-js/irccd/js/plugin.cpp @ 836:e7a37a331753
irccd: pass plugin by reference in javascript api
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 25 Apr 2019 20:59:32 +0200 |
parents | 06cc2f95f479 |
children | a23b7b574ed2 |
line wrap: on
line source
/* * plugin.cpp -- Javascript plugins for irccd * * Copyright (c) 2013-2019 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <cstring> #include <cerrno> #include <fstream> #include <iterator> #include <stdexcept> #include <irccd/daemon/bot.hpp> #include "api.hpp" #include "plugin.hpp" #include "server_api.hpp" using irccd::daemon::bot; using irccd::daemon::connect_event; using irccd::daemon::disconnect_event; using irccd::daemon::invite_event; using irccd::daemon::join_event; using irccd::daemon::kick_event; using irccd::daemon::me_event; using irccd::daemon::message_event; using irccd::daemon::mode_event; using irccd::daemon::names_event; using irccd::daemon::nick_event; using irccd::daemon::notice_event; using irccd::daemon::part_event; using irccd::daemon::plugin; using irccd::daemon::plugin_error; using irccd::daemon::topic_event; using irccd::daemon::whois_event; using irccd::daemon::whois_info; namespace irccd::js { namespace { auto get_metadata(duk::context& ctx, std::string_view name) -> std::string_view { std::string_view ret("unknown"); duk::stack_guard guard(ctx); duk_get_global_string(ctx, "info"); if (duk_get_type(ctx, -1) == DUK_TYPE_OBJECT) { duk_get_prop_string(ctx, -1, name.data()); if (duk_get_type(ctx, -1) == DUK_TYPE_STRING) ret = duk_get_string(ctx, -1); duk_pop(ctx); } duk_pop(ctx); return ret; } auto get_table(duk::context& ctx, std::string_view name) -> plugin::map { plugin::map result; duk::stack_guard sa(ctx); duk_get_global_string(ctx, name.data()); duk_enum(ctx, -1, 0); while (duk_next(ctx, -1, true)) { result.emplace(duk_to_string(ctx, -2), duk_to_string(ctx, -1)); duk_pop_n(ctx, 2); } duk_pop_n(ctx, 2); return result; } void set_table(duk::context& ctx, std::string_view name, const plugin::map& vars) { duk::stack_guard sa(ctx); duk_get_global_string(ctx, name.data()); for (const auto& pair : vars) { duk::push(ctx, pair.second); duk_put_prop_string(ctx, -2, pair.first.c_str()); } duk_pop(ctx); } } // !namespace void plugin::push() noexcept { } template <typename Value, typename... Args> void plugin::push(Value&& value, Args&&... args) { duk::push(context_, std::forward<Value>(value)); push(std::forward<Args>(args)...); } template <typename... Args> void plugin::call(const std::string& func, Args&&... args) { duk::stack_guard sa(context_); duk_get_global_string(context_, func.c_str()); if (duk_get_type(context_, -1) == DUK_TYPE_UNDEFINED) { duk_pop(context_); return; } push(std::forward<Args>(args)...); if (duk_pcall(context_, sizeof... (Args)) != 0) throw plugin_error(plugin_error::exec_error, get_name(), duk::get_stack(context_, -1).get_stack()); duk_pop(context_); } plugin::plugin(std::string id, std::string path) : daemon::plugin(std::move(id)) , path_(path) { duk::stack_guard sa(context_); /* * Create two special tables for configuration and formats, they are * referenced later as * * - Irccd.Plugin.config * - Irccd.Plugin.format * - Irccd.Plugin.paths * * In plugin_module.cpp. */ duk_push_object(context_); duk_put_global_string(context_, config_property.data()); duk_push_object(context_); duk_put_global_string(context_, format_property.data()); duk_push_object(context_); duk_put_global_string(context_, paths_property.data()); duk_push_pointer(context_, this); duk_put_global_string(context_, DUK_HIDDEN_SYMBOL("plugin")); duk::push(context_, path); duk_put_global_string(context_, DUK_HIDDEN_SYMBOL("path")); } auto plugin::get_context() noexcept -> duk::context& { return context_; } auto plugin::get_name() const noexcept -> std::string_view { return get_metadata(context_, "name"); } auto plugin::get_author() const noexcept -> std::string_view { return get_metadata(context_, "author"); } auto plugin::get_license() const noexcept -> std::string_view { return get_metadata(context_, "license"); } auto plugin::get_summary() const noexcept -> std::string_view { return get_metadata(context_, "summary"); } auto plugin::get_version() const noexcept -> std::string_view { return get_metadata(context_, "version"); } auto plugin::get_options() const -> map { return get_table(context_, config_property); } void plugin::set_options(const map& map) { set_table(context_, config_property, map); } auto plugin::get_formats() const -> map { return get_table(context_, format_property); } void plugin::set_formats(const map& map) { set_table(context_, format_property, map); } auto plugin::get_paths() const -> map { return get_table(context_, paths_property); } void plugin::set_paths(const map& map) { set_table(context_, paths_property, map); } void plugin::open() { std::ifstream input(path_); if (!input) throw plugin_error(plugin_error::exec_error, get_name(), std::strerror(errno)); std::string data( std::istreambuf_iterator<char>(input.rdbuf()), std::istreambuf_iterator<char>() ); if (duk_peval_string(context_, data.c_str())) throw plugin_error(plugin_error::exec_error, get_name(), duk::get_stack(context_, -1).get_stack()); } void plugin::handle_command(bot&, const message_event& event) { call("onCommand", event.server, event.origin, event.channel, event.message); } void plugin::handle_connect(bot&, const connect_event& event) { call("onConnect", event.server); } void plugin::handle_disconnect(bot&, const disconnect_event& event) { call("onDisconnect", event.server); } void plugin::handle_invite(bot&, const invite_event& event) { call("onInvite", event.server, event.origin, event.channel); } void plugin::handle_join(bot&, const join_event& event) { call("onJoin", event.server, event.origin, event.channel); } void plugin::handle_kick(bot&, const kick_event& event) { call("onKick", event.server, event.origin, event.channel, event.target, event.reason); } void plugin::handle_load(bot&) { call("onLoad"); } void plugin::handle_message(bot&, const message_event& event) { call("onMessage", event.server, event.origin, event.channel, event.message); } void plugin::handle_me(bot&, const me_event& event) { call("onMe", event.server, event.origin, event.channel, event.message); } void plugin::handle_mode(bot&, const mode_event& event) { call("onMode", event.server, event.origin, event.channel, event.mode, event.limit, event.user, event.mask); } void plugin::handle_names(bot&, const names_event& event) { call("onNames", event.server, event.channel, event.names); } void plugin::handle_nick(bot&, const nick_event& event) { call("onNick", event.server, event.origin, event.nickname); } void plugin::handle_notice(bot&, const notice_event& event) { call("onNotice", event.server, event.origin, event.channel, event.message); } void plugin::handle_part(bot&, const part_event& event) { call("onPart", event.server, event.origin, event.channel, event.reason); } void plugin::handle_reload(bot&) { call("onReload"); } void plugin::handle_topic(bot&, const topic_event& event) { call("onTopic", event.server, event.origin, event.channel, event.topic); } void plugin::handle_unload(bot&) { call("onUnload"); } void plugin::handle_whois(bot&, const whois_event& event) { call("onWhois", event.server, event.whois); } plugin_loader::plugin_loader(bot& bot, std::vector<std::string> directories, std::vector<std::string> extensions) noexcept : daemon::plugin_loader(std::move(directories), std::move(extensions)) , bot_(bot) { } plugin_loader::~plugin_loader() noexcept = default; auto plugin_loader::get_modules() const noexcept -> const modules& { return modules_; } auto plugin_loader::get_modules() noexcept -> modules& { return modules_; } auto plugin_loader::open(std::string_view id, std::string_view path) -> std::shared_ptr<daemon::plugin> { auto plg = std::make_shared<plugin>(std::string(id), std::string(path)); for (const auto& mod : modules_) mod->load(bot_, *plg); plg->open(); return plg; } void duk::type_traits<whois_info>::push(duk_context* ctx, const whois_info& whois) { duk_push_object(ctx); duk::push(ctx, whois.nick); duk_put_prop_string(ctx, -2, "nickname"); duk::push(ctx, whois.user); duk_put_prop_string(ctx, -2, "username"); duk::push(ctx, whois.realname); duk_put_prop_string(ctx, -2, "realname"); duk::push(ctx, whois.hostname); duk_put_prop_string(ctx, -2, "hostname"); duk::push(ctx, whois.channels); duk_put_prop_string(ctx, -2, "channels"); } } // !irccd::js