Mercurial > irccd
view irccdctl/cli.cpp @ 521:e03521cf207b
Common: split util.hpp into more appropriate files, closes #721
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 27 Oct 2017 21:45:32 +0200 |
parents | fbd80bfcf58d |
children | a88796ed040a |
line wrap: on
line source
/* * cli.cpp -- command line for irccdctl * * Copyright (c) 2013-2017 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 <cassert> #include <iostream> #include <sstream> #include <boost/timer/timer.hpp> #include <json.hpp> #include "cli.hpp" #include "irccdctl.hpp" #include "json_util.hpp" #include "logger.hpp" #include "options.hpp" #include "net_util.hpp" #include "string_util.hpp" using namespace std::string_literals; namespace irccd { /* * Cli. * ------------------------------------------------------------------ */ void Cli::check(const nlohmann::json &response) { if (!json_util::get_bool(response, "status", false)) { auto error = json_util::get_string(response, "error"); if (error.empty()) throw std::runtime_error("command failed with an unknown error"); throw std::runtime_error(error); } } nlohmann::json Cli::request(Irccdctl &irccdctl, nlohmann::json args) { auto msg = nlohmann::json(); if (!args.is_object()) args = nlohmann::json::object(); args.push_back({"command", m_name}); irccdctl.client().request(args); auto id = irccdctl.client().onMessage.connect([&] (auto input) { msg = std::move(input); }); try { boost::timer::cpu_timer timer; while (irccdctl.client().isConnected() && !msg.is_object() && timer.elapsed().wall / 1000000LL < 3000) net_util::poll(3000 - timer.elapsed().wall / 1000000LL, irccdctl); } catch (const std::exception &) { irccdctl.client().onMessage.disconnect(id); throw; } irccdctl.client().onMessage.disconnect(id); if (!msg.is_object()) throw std::runtime_error("no response received"); if (json_util::get_string(msg, "command") != m_name) throw std::runtime_error("unexpected command result received"); check(msg); return msg; } void Cli::call(Irccdctl &irccdctl, nlohmann::json args) { check(request(irccdctl, args)); } namespace cli { /* * PluginConfigCli. * ------------------------------------------------------------------ */ void PluginConfigCli::set(Irccdctl &irccdctl, const std::vector<std::string> &args) { check(request(irccdctl, nlohmann::json::object({ { "plugin", args[0] }, { "variable", args[1] }, { "value", args[2] } }))); } void PluginConfigCli::get(Irccdctl &irccdctl, const std::vector<std::string> &args) { auto result = request(irccdctl, nlohmann::json::object({ { "plugin", args[0] }, { "variable", args[1] } })); check(result); if (result["variables"].is_object()) std::cout << json_util::pretty(result["variables"][args[1]]) << std::endl; } void PluginConfigCli::getall(Irccdctl &irccdctl, const std::vector<std::string> &args) { auto result = request(irccdctl, nlohmann::json::object({{ "plugin", args[0] }})); check(result); auto variables = result["variables"]; for (auto v = variables.begin(); v != variables.end(); ++v) std::cout << std::setw(16) << std::left << v.key() << " : " << json_util::pretty(v.value()) << std::endl; } PluginConfigCli::PluginConfigCli() : Cli("plugin-config", "configure a plugin", "plugin-config plugin [variable] [value]", "Get or set a plugin configuration variable.\n\n" "If both variable and value are provided, sets the plugin configuration " "to the\nrespective variable name and value.\n\n" "If only variable is specified, shows its current value. Otherwise, list " "all\nvariables and their values.\n\n" "Examples:\n" "\tirccdctl plugin-config ask") { } void PluginConfigCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { switch (args.size()) { case 3: set(irccdctl, args); break; case 2: get(irccdctl, args); break; case 1: getall(irccdctl, args); break; default: throw std::invalid_argument("plugin-config requires at least 1 argument"); } } /* * PluginInfoCli. * ------------------------------------------------------------------ */ PluginInfoCli::PluginInfoCli() : Cli("plugin-info", "get plugin information", "plugin-info plugin", "Get plugin information.\n\n" "Example:\n" "\tirccdctl plugin-info ask" ) { } void PluginInfoCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 1) throw std::invalid_argument("plugin-info requires 1 argument"); auto result = request(irccdctl, {{ "plugin", args[0] }}); std::cout << std::boolalpha; std::cout << "Author : " << json_util::get_string(result, "author") << std::endl; std::cout << "License : " << json_util::get_string(result, "license") << std::endl; std::cout << "Summary : " << json_util::get_string(result, "summary") << std::endl; std::cout << "Version : " << json_util::get_string(result, "version") << std::endl; } /* * PluginListCli. * ------------------------------------------------------------------ */ PluginListCli::PluginListCli() : Cli("plugin-list", "list loaded plugins", "plugin-list", "Get the list of all loaded plugins.\n\n" "Example:\n" "\tirccdctl plugin-list") { } void PluginListCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &) { auto result = request(irccdctl); for (const auto &value : result["list"]) if (value.is_string()) std::cout << value.get<std::string>() << std::endl; } /* * PluginLoadCli. * ------------------------------------------------------------------ */ PluginLoadCli::PluginLoadCli() : Cli("plugin-load", "load a plugin", "plugin-load logger", "Load a plugin into the irccd instance.\n\n" "Example:\n" "\tirccdctl plugin-load logger") { } void PluginLoadCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 1) throw std::invalid_argument("plugin-load requires 1 argument"); check(request(irccdctl, {{"plugin", args[0]}})); } /* * PluginReloadCli. * ------------------------------------------------------------------ */ PluginReloadCli::PluginReloadCli() : Cli("plugin-reload", "reload a plugin", "plugin-reload plugin", "Reload a plugin by calling the appropriate onReload event, the plugin is not\n" "unloaded and must be already loaded.\n\n" "Example:\n" "\tirccdctl plugin-reload logger") { } void PluginReloadCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 1) throw std::invalid_argument("plugin-reload requires 1 argument"); check(request(irccdctl, {{ "plugin", args[0] }})); } /* * PluginUnloadCli. * ------------------------------------------------------------------ */ PluginUnloadCli::PluginUnloadCli() : Cli("plugin-unload", "unload a plugin", "plugin-unload plugin", "Unload a loaded plugin from the irccd instance.\n\n" "Example:\n" "\tirccdctl plugin-unload logger") { } void PluginUnloadCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 1) throw std::invalid_argument("plugin-unload requires 1 argument"); check(request(irccdctl, {{ "plugin", args[0] }})); } /* * ServerChannelCli. * ------------------------------------------------------------------ */ ServerChannelMode::ServerChannelMode() : Cli("server-cmode", "change channel mode", "server-cmode server channel mode", "Change the mode of the specified channel.\n\n" "Example:\n" "\tirccdctl server-cmode freenode #staff +t") { } void ServerChannelMode::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 3) throw std::invalid_argument("server-cmode requires 3 arguments"); check(request(irccdctl, { { "command", "server-cmode" }, { "server", args[0] }, { "channel", args[1] }, { "mode", args[2] } })); } /* * ServerChannelNoticeCli. * ------------------------------------------------------------------ */ ServerChannelNoticeCli::ServerChannelNoticeCli() : Cli("server-cnotice", "send a channel notice", "server-cnotice server channel message", "Send a notice to a public channel. This is a notice that everyone on the channel\n" "will receive.\n\n" "Example:\n" "\tirccdctl server-cnotice freenode #staff \"Don't flood!\"") { } void ServerChannelNoticeCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 3) throw std::invalid_argument("server-cnotice requires 3 arguments"); check(request(irccdctl, { { "command", "server-cnotice" }, { "server", args[0] }, { "channel", args[1] }, { "message", args[2] } })); } /* * ServerConnectCli. * ------------------------------------------------------------------ */ namespace { option::result parse(std::vector<std::string> &args) { option::options options{ { "-c", true }, { "--command", true }, { "-n", true }, { "--nickname", true }, { "-r", true }, { "--realname", true }, { "-S", false }, { "--ssl-verify", false }, { "-s", false }, { "--ssl", false }, { "-u", true }, { "--username", true } }; return option::read(args, options); } } // !namespace ServerConnectCli::ServerConnectCli() : Cli("server-connect", "add a server", "server-connect [options] id host [port]", "Connect to a new IRC server.\n\n" "Available options:\n" " -c, --command\t\tspecify the command char\n" " -n, --nickname\tspecify a nickname\n" " -r, --realname\tspecify a real name\n" " -S, --ssl-verify\tverify SSL\n" " -s, --ssl\t\tconnect using SSL\n" " -u, --username\tspecify a user name\n\n" "Example:\n" "\tirccdctl server-connect -n jean example irc.example.org\n" "\tirccdctl server-connect --ssl example irc.example.org 6697") { } void ServerConnectCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { std::vector<std::string> copy(args); option::result result = parse(copy); option::result::const_iterator it; if (copy.size() < 2) throw std::invalid_argument("server-connect requires at least 2 arguments"); auto object = nlohmann::json::object({ { "name", copy[0] }, { "host", copy[1] } }); if (copy.size() == 3) { if (!string_util::is_int(copy[2])) throw std::invalid_argument("invalid port number"); object["port"] = std::stoi(copy[2]); } if (result.count("-S") > 0 || result.count("--ssl-verify") > 0) object["sslVerify"] = true; if (result.count("-s") > 0 || result.count("--ssl") > 0) object["ssl"] = true; if ((it = result.find("-n")) != result.end() || (it = result.find("--nickname")) != result.end()) object["nickname"] = it->second; if ((it = result.find("-r")) != result.end() || (it = result.find("--realname")) != result.end()) object["realname"] = it->second; if ((it = result.find("-u")) != result.end() || (it = result.find("--username")) != result.end()) object["username"] = it->second; check(request(irccdctl, object)); } /* * ServerDisconnectCli. * ------------------------------------------------------------------ */ ServerDisconnectCli::ServerDisconnectCli() : Cli("server-disconnect", "disconnect server", "server-disconnect [server]", "Disconnect from a server.\n\n" "If server is not specified, irccd disconnects all servers.\n\n" "Example:\n" "\tirccdctl server-disconnect localhost") { } void ServerDisconnectCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { auto object = nlohmann::json::object({ { "command", "server-disconnect" } }); if (args.size() > 0) object["server"] = args[0]; check(request(irccdctl, object)); } /* * ServerInfoCli. * ------------------------------------------------------------------ */ ServerInfoCli::ServerInfoCli() : Cli("server-info", "get server information", "server-info server", "Get information about a server.\n\n" "Example:\n" "\tirccdctl server-info freenode") { } void ServerInfoCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 1) throw std::invalid_argument("server-info requires 1 argument"); auto result = request(irccdctl, { { "command", "server-info" }, { "server", args[0] } }); check(result); std::cout << std::boolalpha; std::cout << "Name : " << json_util::pretty(result["name"]) << std::endl; std::cout << "Host : " << json_util::pretty(result["host"]) << std::endl; std::cout << "Port : " << json_util::pretty(result["port"]) << std::endl; std::cout << "Ipv6 : " << json_util::pretty(result["ipv6"]) << std::endl; std::cout << "SSL : " << json_util::pretty(result["ssl"]) << std::endl; std::cout << "SSL verified : " << json_util::pretty(result["sslVerify"]) << std::endl; std::cout << "Channels : "; for (const auto &v : result["channels"]) if (v.is_string()) std::cout << v.get<std::string>() << " "; std::cout << std::endl; std::cout << "Nickname : " << json_util::pretty(result["nickname"]) << std::endl; std::cout << "User name : " << json_util::pretty(result["username"]) << std::endl; std::cout << "Real name : " << json_util::pretty(result["realname"]) << std::endl; } /* * ServerInviteCli. * ------------------------------------------------------------------ */ ServerInviteCli::ServerInviteCli() : Cli("server-invite", "invite someone", "server-invite server nickname channel", "Invite the specified target on the channel.\n\n" "Example:\n" "\tirccdctl server-invite freenode xorg62 #staff") { } void ServerInviteCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 3) throw std::invalid_argument("server-invite requires 3 arguments"); check(request(irccdctl, { { "command", "server-invite" }, { "server", args[0] }, { "target", args[1] }, { "channel", args[2] } })); } /* * ServerJoinCli. * ------------------------------------------------------------------ */ ServerJoinCli::ServerJoinCli() : Cli("server-join", "join a channel", "server-join server channel [password]", "Join the specified channel, the password is optional.\n\n" "Example:\n" "\tirccdctl server-join freenode #test\n" "\tirccdctl server-join freenode #private-club secret") { } void ServerJoinCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 2) throw std::invalid_argument("server-join requires at least 2 arguments"); auto object = nlohmann::json::object({ { "server", args[0] }, { "channel", args[1] } }); if (args.size() == 3) object["password"] = args[2]; check(request(irccdctl, object)); } /* * ServerKickCli. * ------------------------------------------------------------------ */ ServerKickCli::ServerKickCli() : Cli("server-kick", "kick someone from a channel", "server-kick server target channel [reason]", "Kick the specified target from the channel, the reason is optional.\n\n" "Example:\n" "\tirccdctl server-kick freenode jean #staff \"Stop flooding\"") { } void ServerKickCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 3) throw std::invalid_argument("server-kick requires at least 3 arguments "); auto object = nlohmann::json::object({ { "server", args[0] }, { "target", args[1] }, { "channel", args[2] } }); if (args.size() == 4) object["reason"] = args[3]; check(request(irccdctl, object)); } /* * ServerListCli. * ------------------------------------------------------------------ */ ServerListCli::ServerListCli() : Cli("server-list", "get list of servers", "server-list", "Get the list of all connected servers.\n\n" "Example:\n" "\tirccdctl server-list") { } void ServerListCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &) { auto response = request(irccdctl); check(response); for (const auto &n : response["list"]) if (n.is_string()) std::cout << n.get<std::string>() << std::endl; } /* * ServerMeCli. * ------------------------------------------------------------------ */ ServerMeCli::ServerMeCli() : Cli("server-me", "send an action emote", "server-me server target message", "Send an action emote.\n\n" "Example:\n" "\tirccdctl server-me freenode #staff \"going back soon\"") { } void ServerMeCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 3) throw std::runtime_error("server-me requires 3 arguments"); check(request(irccdctl, { { "server", args[0] }, { "target", args[1] }, { "message", args[2] } })); } /* * ServerMessageCli. * ------------------------------------------------------------------ */ ServerMessageCli::ServerMessageCli() : Cli("server-message", "send a message", "server-message server target message", "Send a message to the specified target or channel.\n\n" "Example:\n" "\tirccdctl server-message freenode #staff \"Hello from irccd\"") { } void ServerMessageCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 3) throw std::invalid_argument("server-message requires 3 arguments"); check(request(irccdctl, { { "server", args[0] }, { "target", args[1] }, { "message", args[2] } })); } /* * ServerModeCli. * ------------------------------------------------------------------ */ ServerModeCli::ServerModeCli() : Cli("server-mode", "the the user mode", "server-mode server mode", "Set the irccd's user mode.\n\n" "Example:\n" "\tirccdctl server-mode +i") { } void ServerModeCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 2) throw std::invalid_argument("server-mode requires 2 arguments"); check(request(irccdctl, { { "server", args[0] }, { "mode", args[1] } })); } /* * ServerNickCli. * ------------------------------------------------------------------ */ ServerNickCli::ServerNickCli() : Cli("server-nick", "change your nickname", "server-nick server nickname", "Change irccd's nickname.\n\n" "Example:\n" "\tirccdctl server-nick freenode david") { } void ServerNickCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 2) throw std::invalid_argument("server-nick requires 2 arguments"); check(request(irccdctl, { { "server", args[0] }, { "nickname", args[1] } })); } /* * ServerNoticeCli. * ------------------------------------------------------------------ */ ServerNoticeCli::ServerNoticeCli() : Cli("server-notice", "send a private notice", "server-notice server target message", "Send a private notice to the specified target.\n\n" "Example:\n" "\tirccdctl server-notice freenode jean \"I know you are here.\"") { } void ServerNoticeCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 3) throw std::invalid_argument("server-notice requires 3 arguments"); check(request(irccdctl, { { "server", args[0] }, { "target", args[1] }, { "message", args[2] } })); } /* * ServerPartCli. * ------------------------------------------------------------------ */ ServerPartCli::ServerPartCli() : Cli("server-part", "leave a channel", "server-part server channel [reason]", "Leave the specified channel, the reason is optional.\n\n" "Not all IRC servers support giving a reason to leave a channel, do not specify\n" "it if this is a concern.\n\n" "Example:\n" "\tirccdctl server-part freenode #staff\n" "\tirccdctl server-part freenode #botwar \"too noisy\"") { } void ServerPartCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 2) throw std::invalid_argument("server-part requires at least 2 arguments"); auto object = nlohmann::json::object({ { "server", args[0] }, { "channel", args[1] } }); if (args.size() >= 3) object["reason"] = args[2]; check(request(irccdctl, object)); } /* * ServerReconnectCli. * ------------------------------------------------------------------ */ ServerReconnectCli::ServerReconnectCli() : Cli("server-reconnect", "force reconnection of a server", "server-reconnect [server]", "Force reconnection of one or all servers.\n\n" "If server is not specified, all servers will try to reconnect.\n\n" "Example:\n" "\tirccdctl server-reconnect\n" "\tirccdctl server-reconnect wanadoo") { } void ServerReconnectCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { auto object = nlohmann::json::object({ { "command", "server-reconnect" } }); if (args.size() >= 1) object["server"] = args[0]; check(request(irccdctl, object)); } /* * ServerTopicCli. * ------------------------------------------------------------------ */ ServerTopicCli::ServerTopicCli() : Cli("server-topic", "change channel topic", "server-topic server channel topic", "Change the topic of the specified channel.\n\n" "Example:\n" "\tirccdctl server-topic freenode #wmfs \"This is the best channel\"") { } void ServerTopicCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 3) throw std::invalid_argument("server-topic requires 3 arguments"); check(request(irccdctl, { { "server", args[0] }, { "channel", args[1] }, { "topic", args[2] } })); } /* * RuleAddCli. * ------------------------------------------------------------------ */ RuleAddCli::RuleAddCli() : Cli("rule-add", "add a new rule", "rule-add [options] accept|drop", "Add a new rule to irccd.\n\n" "If no index is specified, the rule is added to the end.\n\n" "Available options:\n" " -c, --add-channel\t\tmatch a channel\n" " -e, --add-event\t\tmatch an event\n" " -i, --index\t\t\trule position\n" " -p, --add-plugin\t\tmatch a plugin\n" " -s, --add-server\t\tmatch a server\n\n" "Example:\n" "\tirccdctl rule-add -p hangman drop\n" "\tirccdctl rule-add -s localhost -c #games -p hangman accept") { } void RuleAddCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { static const option::options options{ { "-c", true }, { "--add-channel", true }, { "-e", true }, { "--add-event", true }, { "-i", true }, { "--index", true }, { "-p", true }, { "--add-plugin", true }, { "-s", true }, { "--add-server", true } }; auto copy = args; auto result = option::read(copy, options); if (copy.size() < 1) throw std::invalid_argument("rule-add requires at least 1 argument"); auto json = nlohmann::json::object({ { "command", "rule-add" }, { "channels", nlohmann::json::array() }, { "events", nlohmann::json::array() }, { "plugins", nlohmann::json::array() }, { "servers", nlohmann::json::array() } }); // All sets. for (const auto& pair : result) { if (pair.first == "-c" || pair.first == "--add-channel") json["channels"].push_back(pair.second); if (pair.first == "-e" || pair.first == "--add-event") json["events"].push_back(pair.second); if (pair.first == "-p" || pair.first == "--add-plugin") json["plugins"].push_back(pair.second); if (pair.first == "-s" || pair.first == "--add-server") json["servers"].push_back(pair.second); } // Index. if (result.count("-i") > 0) json["index"] = string_util::to_number<unsigned>(result.find("-i")->second); if (result.count("--index") > 0) json["index"] = string_util::to_number<unsigned>(result.find("--index")->second); // And action. if (copy[0] != "accept" && copy[0] != "drop") throw std::runtime_error("invalid action '"s + copy[0] + "'"); json["action"] = copy[0]; check(request(irccdctl, json)); } /* * RuleEditCli. * ------------------------------------------------------------------ */ RuleEditCli::RuleEditCli() : Cli("rule-edit", "edit an existing rule", "rule-edit [options] index", "Edit an existing rule in irccd.\n\n" "All options can be specified multiple times.\n\n" "Available options:\n" " -a, --action\t\t\tset action\n" " -c, --add-channel\t\tmatch a channel\n" " -C, --remove-channel\t\tremove a channel\n" " -e, --add-event\t\tmatch an event\n" " -E, --remove-event\t\tremove an event\n" " -p, --add-plugin\t\tmatch a plugin\n" " -P, --add-plugin\t\tremove a plugin\n" " -s, --add-server\t\tmatch a server\n" " -S, --remove-server\t\tremove a server\n\n" "Example:\n" "\tirccdctl rule-edit -p hangman 0\n" "\tirccdctl rule-edit -S localhost -c #games -p hangman 1") { } void RuleEditCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { static const option::options options{ { "-a", true }, { "--action", true }, { "-c", true }, { "--add-channel", true }, { "-C", true }, { "--remove-channel", true }, { "-e", true }, { "--add-event", true }, { "-E", true }, { "--remove-event", true }, { "-p", true }, { "--add-plugin", true }, { "-P", true }, { "--remove-plugin", true }, { "-s", true }, { "--add-server", true }, { "-S", true }, { "--remove-server", true }, }; auto copy = args; auto result = option::read(copy, options); if (copy.size() < 1) throw std::invalid_argument("rule-edit requires at least 1 argument"); auto json = nlohmann::json::object({ { "command", "rule-edit" }, { "channels", nlohmann::json::array() }, { "events", nlohmann::json::array() }, { "plugins", nlohmann::json::array() }, { "servers", nlohmann::json::array() } }); for (const auto& pair : result) { // Action. if (pair.first == "-a" || pair.first == "--action") json["action"] = pair.second; // Additions. if (pair.first == "-c" || pair.first == "--add-channel") json["add-channels"].push_back(pair.second); if (pair.first == "-e" || pair.first == "--add-event") json["add-events"].push_back(pair.second); if (pair.first == "-p" || pair.first == "--add-plugin") json["add-plugins"].push_back(pair.second); if (pair.first == "-s" || pair.first == "--add-server") json["add-servers"].push_back(pair.second); // Removals. if (pair.first == "-C" || pair.first == "--remove-channel") json["remove-channels"].push_back(pair.second); if (pair.first == "-E" || pair.first == "--remove-event") json["remove-events"].push_back(pair.second); if (pair.first == "-P" || pair.first == "--remove-plugin") json["remove-plugins"].push_back(pair.second); if (pair.first == "-S" || pair.first == "--remove-server") json["remove-servers"].push_back(pair.second); } // Index. json["index"] = string_util::to_number<unsigned>(copy[0]); check(request(irccdctl, json)); } /* * RuleListCli. * ------------------------------------------------------------------ */ namespace { void showRule(const nlohmann::json& json, int index) { assert(json.is_object()); auto unjoin = [] (auto array) { std::ostringstream oss; for (auto it = array.begin(); it != array.end(); ++it) { if (!it->is_string()) continue; oss << it->template get<std::string>() << " "; } return oss.str(); }; auto unstr = [] (auto action) { if (action.is_string() && action == "accept") return "accept"; else return "drop"; }; std::cout << "rule: " << index << std::endl; std::cout << "servers: " << unjoin(json["servers"]) << std::endl; std::cout << "channels: " << unjoin(json["channels"]) << std::endl; std::cout << "plugins: " << unjoin(json["plugins"]) << std::endl; std::cout << "events: " << unjoin(json["events"]) << std::endl; std::cout << "action: " << unstr(json["action"]) << std::endl; std::cout << std::endl; } } // !namespace RuleListCli::RuleListCli() : Cli("rule-list", "list all rules", "rule-list", "List all rules.\n\n" "Example:\n" "\tirccdctl rule-list") { } void RuleListCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &) { auto response = request(irccdctl); auto pos = 0; check(response); for (const auto &obj : response["list"]) { if (!obj.is_object()) continue; showRule(obj, pos++); } } /* * RuleInfoCli. * ------------------------------------------------------------------ */ RuleInfoCli::RuleInfoCli() : Cli("rule-info", "show a rule", "rule-info index", "Show a rule.\n\n" "Example:\n" "\tirccdctl rule-info 0\n" "\tirccdctl rule-info 1") { } void RuleInfoCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 1) throw std::invalid_argument("rule-info requires 1 argument"); int index = 0; try { index = std::stoi(args[0]); } catch (...) { throw std::invalid_argument("invalid number '" + args[0] + "'"); } auto result = request(irccdctl, { { "command", "rule-info" }, { "index", index } }); check(result); showRule(result, 0); } /* * RuleRemoveCli. * ------------------------------------------------------------------ */ RuleRemoveCli::RuleRemoveCli() : Cli("rule-remove", "remove a rule", "rule-remove index", "Remove an existing rule.\n\n" "Example:\n" "\tirccdctl rule-remove 0\n" "\tirccdctl rule-remove 1") { } void RuleRemoveCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 1) throw std::invalid_argument("rule-remove requires 1 argument"); int index = 0; try { index = std::stoi(args[0]); } catch (...) { throw std::invalid_argument("invalid number '" + args[0] + "'"); } auto result = request(irccdctl, { { "command", "rule-remove" }, { "index", index } }); check(result); } /* * RuleMoveCli. * ------------------------------------------------------------------ */ RuleMoveCli::RuleMoveCli() : Cli("rule-move", "move a rule to a new position", "rule-move source destination", "Move a rule from the given source at the specified destination index.\n\n" "The rule will replace the existing one at the given destination moving\ndown every " "other rules. If destination is greater or equal the number of rules,\nthe rule " "is moved to the end.\n\n" "Example:\n" "\tirccdctl rule-move 0 5\n" "\tirccdctl rule-move 4 3") { } void RuleMoveCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args) { if (args.size() < 2) throw std::invalid_argument("rule-move requires 2 arguments"); int from = 0; int to = 0; try { from = std::stoi(args[0]); to = std::stoi(args[1]); } catch (...) { throw std::invalid_argument("invalid number"); } check(request(irccdctl, { { "command", "rule-move" }, { "from", from }, { "to", to } })); } /* * WatchCli. * ------------------------------------------------------------------ */ namespace { std::string format(std::vector<std::string> args) { auto result = option::read(args, { { "-f", true }, { "--format", true } }); if (result.count("-f") > 0) return result.find("-f")->second; if (result.count("--format") > 0) return result.find("--format")->second; return "native"; } void onChannelMode(const nlohmann::json &v) { std::cout << "event: onChannelMode\n"; std::cout << "server: " << json_util::pretty(v, "server") << "\n"; std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; std::cout << "mode: " << json_util::pretty(v, "mode") << "\n"; std::cout << "argument: " << json_util::pretty(v, "argument") << "\n"; } void onChannelNotice(const nlohmann::json &v) { std::cout << "event: onChannelNotice\n"; std::cout << "server: " << json_util::pretty(v, "server") << "\n"; std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; std::cout << "channel: " << json_util::pretty(v, "channel") << "\n"; std::cout << "message: " << json_util::pretty(v, "message") << "\n"; } void onConnect(const nlohmann::json &v) { std::cout << "event: onConnect\n"; std::cout << "server: " << json_util::pretty(v, "server") << "\n"; } void onInvite(const nlohmann::json &v) { std::cout << "event: onInvite\n"; std::cout << "server: " << json_util::pretty(v, "server") << "\n"; std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; std::cout << "channel: " << json_util::pretty(v, "channel") << "\n"; } void onJoin(const nlohmann::json &v) { std::cout << "event: onJoin\n"; std::cout << "server: " << json_util::pretty(v, "server") << "\n"; std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; std::cout << "channel: " << json_util::pretty(v, "channel") << "\n"; } void onKick(const nlohmann::json &v) { std::cout << "event: onKick\n"; std::cout << "server: " << json_util::pretty(v, "server") << "\n"; std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; std::cout << "channel: " << json_util::pretty(v, "channel") << "\n"; std::cout << "target: " << json_util::pretty(v, "target") << "\n"; std::cout << "reason: " << json_util::pretty(v, "reason") << "\n"; } void onMessage(const nlohmann::json &v) { std::cout << "event: onMessage\n"; std::cout << "server: " << json_util::pretty(v, "server") << "\n"; std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; std::cout << "channel: " << json_util::pretty(v, "channel") << "\n"; std::cout << "message: " << json_util::pretty(v, "message") << "\n"; } void onMe(const nlohmann::json &v) { std::cout << "event: onMe\n"; std::cout << "server: " << json_util::pretty(v, "server") << "\n"; std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; std::cout << "target: " << json_util::pretty(v, "target") << "\n"; std::cout << "message: " << json_util::pretty(v, "message") << "\n"; } void onMode(const nlohmann::json &v) { std::cout << "event: onMode\n"; std::cout << "server: " << json_util::pretty(v, "server") << "\n"; std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; std::cout << "mode: " << json_util::pretty(v, "mode") << "\n"; } void onNames(const nlohmann::json &v) { std::cout << "event: onNames\n"; std::cout << "server: " << json_util::pretty(v, "server") << "\n"; std::cout << "channel: " << json_util::pretty(v, "channel") << "\n"; std::cout << "names: " << json_util::pretty(v, "names") << "\n"; } void onNick(const nlohmann::json &v) { std::cout << "event: onNick\n"; std::cout << "server: " << json_util::pretty(v, "server") << "\n"; std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; std::cout << "nickname: " << json_util::pretty(v, "nickname") << "\n"; } void onNotice(const nlohmann::json &v) { std::cout << "event: onNotice\n"; std::cout << "server: " << json_util::pretty(v, "server") << "\n"; std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; std::cout << "message: " << json_util::pretty(v, "message") << "\n"; } void onPart(const nlohmann::json &v) { std::cout << "event: onPart\n"; std::cout << "server: " << json_util::pretty(v, "server") << "\n"; std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; std::cout << "channel: " << json_util::pretty(v, "channel") << "\n"; std::cout << "reason: " << json_util::pretty(v, "reason") << "\n"; } void onQuery(const nlohmann::json &v) { std::cout << "event: onQuery\n"; std::cout << "server: " << json_util::pretty(v, "server") << "\n"; std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; std::cout << "message: " << json_util::pretty(v, "message") << "\n"; } void onTopic(const nlohmann::json &v) { std::cout << "event: onTopic\n"; std::cout << "server: " << json_util::pretty(v, "server") << "\n"; std::cout << "origin: " << json_util::pretty(v, "origin") << "\n"; std::cout << "channel: " << json_util::pretty(v, "channel") << "\n"; std::cout << "topic: " << json_util::pretty(v, "topic") << "\n"; } void onWhois(const nlohmann::json &v) { std::cout << "event: onWhois\n"; std::cout << "server: " << json_util::pretty(v, "server") << "\n"; std::cout << "nickname: " << json_util::pretty(v, "nickname") << "\n"; std::cout << "username: " << json_util::pretty(v, "username") << "\n"; std::cout << "host: " << json_util::pretty(v, "host") << "\n"; std::cout << "realname: " << json_util::pretty(v, "realname") << "\n"; } const std::unordered_map<std::string, std::function<void (const nlohmann::json &)>> events{ { "onChannelMode", onChannelMode }, { "onChannelNotice", onChannelNotice }, { "onConnect", onConnect }, { "onInvite", onInvite }, { "onJoin", onJoin }, { "onKick", onKick }, { "onMessage", onMessage }, { "onMe", onMe }, { "onMode", onMode }, { "onNames", onNames }, { "onNick", onNick }, { "onNotice", onNotice }, { "onPart", onPart }, { "onQuery", onQuery }, { "onTopic", onTopic }, { "onWhois", onWhois } }; } // !namespace WatchCli::WatchCli() : Cli("watch", "watch irccd events", "watch [-f|--format json|native]", "Start watching irccd events.\n\n" "You can use different output formats, native is human readable format, json is\n" "pretty formatted json.\n\n" "Example:\n" "\tirccdctl watch\n" "\tirccdctl watch -f json") { } void WatchCli::exec(Irccdctl& client, const std::vector<std::string>& args) { std::string fmt = format(args); if (fmt != "native" && fmt != "json") throw std::invalid_argument("invalid format given: " + fmt); auto id = client.client().onEvent.connect([&] (auto ev) { try { auto name = ev.find("event"); if (name == ev.end() || !name->is_string()) return; auto it = events.find(*name); // Silently ignore to avoid breaking user output. if (it == events.end()) return; if (fmt == "json") std::cout << ev.dump(4) << std::endl; else { it->second(ev); std::cout << std::endl; } } catch (...) { } }); try { while (client.client().isConnected()) { net_util::poll(500, client); } } catch (const std::exception &ex) { log::warning() << ex.what() << std::endl; } client.client().onEvent.disconnect(id); } } // !cli } // !irccd