diff --git a/driver/main.cpp b/driver/main.cpp index b981055..2a0d317 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -60,7 +60,11 @@ auto configure_sasl(const Settings &settings) -> std::unique_ptr return nullptr; } -static auto start(boost::asio::io_context &io, const Settings &settings) -> void +static auto start( + boost::asio::io_context &io, + const Settings &settings, + std::shared_ptr webhook +) -> void { Ref tls_cert; if (settings.use_tls && not settings.tls_cert_file.empty()) @@ -95,13 +99,18 @@ static auto start(boost::asio::io_context &io, const Settings &settings) -> void } } + client->sig_registered.connect([connection, webhook]() { + webhook->set_connection(connection); + }); + // On disconnect reconnect in 5 seconds // connection is captured in the disconnect handler so it can keep itself alive connection->sig_disconnect.connect( - [&io, &settings, connection]() { + [&io, &settings, connection, webhook]() { + webhook->clear_connection(); auto timer = std::make_shared(io); timer->expires_after(5s); - timer->async_wait([&io, &settings, timer](auto) { start(io, settings); }); + timer->async_wait([&io, &settings, timer, webhook](auto) { start(io, settings, webhook); }); } ); @@ -141,12 +150,15 @@ auto main(int argc, char *argv[]) -> int { //boost::log::core::get()->set_filter(boost::log::trivial::severity >= boost::log::trivial::warning); - if (argc != 2) { + if (argc != 3) { BOOST_LOG_TRIVIAL(error) << "Bad arguments"; return 1; } const auto settings = get_settings(argv[1]); auto io = boost::asio::io_context{}; - start(io, settings); + + auto webhooks = start_webhook(io, argv[2]); + + start(io, settings, webhooks); io.run(); } diff --git a/driver/web.cpp b/driver/web.cpp index 10f32b6..7aca4ad 100644 --- a/driver/web.cpp +++ b/driver/web.cpp @@ -1 +1,263 @@ #include "web.hpp" + +#include +#include + +#include +#include +#include + +namespace beast = boost::beast; // from +namespace http = beast::http; // from +namespace net = boost::asio; // from +namespace websocket = beast::websocket; +using tcp = net::ip::tcp; // from + +using namespace std::literals; + +namespace { + +auto report_error(std::exception_ptr eptr) -> void +{ + if (eptr) + { + try + { + std::rethrow_exception(eptr); + } + catch (const std::exception &e) + { + BOOST_LOG_TRIVIAL(error) << "An error occurred: " << e.what(); + } + } +} + +template +auto handle_request( + std::shared_ptr self, + http::request> &&req +) -> http::message_generator +{ + self->add_event({"project", "message"}); + + http::response res{http::int_to_status(200), req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.keep_alive(req.keep_alive()); + + std::string reply_text = "Hello, world!"; + res.content_length(reply_text.size()); + res.body() = std::move(reply_text); + + return res; +} + +auto read_loop(tcp::socket socket, std::shared_ptr self) -> boost::asio::awaitable +{ + beast::tcp_stream stream{std::move(socket)}; + beast::flat_buffer buffer; + http::request req; + + for (;;) + { + req.clear(); + stream.expires_after(30s); + + boost::system::error_code ec; + co_await http::async_read(stream, buffer, req, net::redirect_error(net::use_awaitable, ec)); + if (ec == http::error::end_of_stream) + { + stream.socket().shutdown(tcp::socket::shutdown_send, ec); + co_return; + } + else if (ec) + { + co_return; + } + + auto msg = handle_request(self, std::move(req)); + const auto keep_alive = msg.keep_alive(); + + co_await beast::async_write(stream, std::move(msg), net::use_awaitable); + + if (!keep_alive) + { + stream.socket().shutdown(tcp::socket::shutdown_send, ec); + co_return; + } + } +} + +auto accept_loop( + tcp::acceptor acceptor, + std::shared_ptr self +) -> boost::asio::awaitable +{ + for (;;) + { + auto socket = co_await acceptor.async_accept(net::use_awaitable); + boost::asio::co_spawn( + acceptor.get_executor(), + read_loop(std::move(socket), self), + report_error + ); + } +} + +auto spawn_webhook( + boost::asio::io_context &io, + const std::shared_ptr webhook +) -> boost::asio::awaitable +{ + tcp::resolver resolver{io}; + auto results = co_await resolver.async_resolve(webhook->settings_.host, webhook->settings_.service, tcp::resolver::passive, boost::asio::use_awaitable); + + for (auto &&result : results) + { + const auto endpoint = result.endpoint(); + BOOST_LOG_TRIVIAL(info) << "HTTP: Listening on " << endpoint; + tcp::acceptor acceptor{io}; + acceptor.open(endpoint.protocol()); + acceptor.set_option(net::socket_base::reuse_address(true)); + acceptor.bind(endpoint); + acceptor.listen(net::socket_base::max_listen_connections); + boost::asio::co_spawn(io, accept_loop(std::move(acceptor), webhook), report_error); + } +} + +} // namespace + +auto start_webhook( + boost::asio::io_context &io, + const char * webhook_settings_filename +) -> std::shared_ptr +{ + std::ifstream webhook_settings_file{webhook_settings_filename}; + if (!webhook_settings_file) + { + BOOST_LOG_TRIVIAL(error) << "Unable to open webhook settings file"; + std::exit(1); + } + + auto webhook_settings = toml::parse(webhook_settings_file); + WebhookSettings settings = WebhookSettings::from_toml(webhook_settings); + BOOST_LOG_TRIVIAL(info) << "Webhook settings: " << settings.to_toml(); + auto webhook = std::make_shared(std::move(settings)); + boost::asio::co_spawn(io, spawn_webhook(io, webhook), report_error); + return webhook; +} + +auto GithubWebhook::write_event(WebhookEvent event) -> void +{ + connection_->send_notice("glguy", event.channel + ": " + event.message); +} + +auto ProjectSettings::from_toml(const toml::table &v) -> ProjectSettings +{ + ProjectSettings result; + result.channel = v["channel"].value_or(""s); + result.credential_name = v["credential_name"].value_or(""s); + result.enabled = v["enabled"].value_or(false); + + if (const auto events = v["events"].as_array()) + { + for (const auto &event : *events) + { + result.events.insert(event.value_or(""s)); + } + } + + if (const auto accounts = v["authorized_accounts"].as_array()) + { + for (const auto &account : *accounts) + { + result.authorized_accounts.insert(account.value_or(""s)); + } + } + + return result; +} + +auto ProjectSettings::to_toml() const -> toml::table +{ + toml::array events_array; + for (const auto &event : events) + { + events_array.emplace_back(event); + } + + toml::array authorized_accounts_array; + for (const auto &account : authorized_accounts) + { + authorized_accounts_array.emplace_back(account); + } +\ + return toml::table{ + {"channel", channel}, + {"credential_name", credential_name}, + {"enabled", enabled}, + {"events", std::move(events_array)}, + {"authorized_accounts", std::move(authorized_accounts_array)} + }; +} + +auto WebhookSettings::from_toml(const toml::table &v) -> WebhookSettings +{ + WebhookSettings result; + result.host = v["host"].value_or(""s); + result.service = v["service"].value_or("http"s); + + if (const auto credentials = v["credentials"].as_array()) + { + for (const auto &credential : *credentials) + { + if (auto credential_table = credential.as_table()) + { + result.credentials.emplace( + (*credential_table)["name"].value_or(""s), + (*credential_table)["key"].value_or(""s)); + } + } + } + + if (const auto projects = v["projects"].as_array()) + { + for (const auto &project : *projects) + { + if (auto project_table = project.as_table()) + { + result.projects.emplace( + (*project_table)["name"].value_or(""s), + ProjectSettings::from_toml(*project_table)); + } + } + } + + return result; +} + +auto WebhookSettings::to_toml() const -> toml::table +{ + toml::array credential_tables; + for (const auto &[name, key] : credentials) + { + credential_tables.emplace_back(toml::table { + {"name", name}, + {"key", key} + }); + } + + toml::array project_tables; + for (const auto &[name, project] : projects) + { + auto tab = project.to_toml(); + tab.emplace("name", name); + project_tables.emplace_back(std::move(tab)); + } + + return toml::table{ + {"host", host}, + {"service", service}, + {"credentials", std::move(credential_tables)}, + {"projects", std::move(project_tables)} + }; +} \ No newline at end of file diff --git a/driver/web.hpp b/driver/web.hpp index 6f70f09..656a31a 100644 --- a/driver/web.hpp +++ b/driver/web.hpp @@ -1 +1,100 @@ #pragma once + +#include + +#include + +#include +#include + +#include +#include +#include + +struct ProjectSettings { + // *** Administrative settings *** + + // IRC channel to announce to + std::string channel; + + // name extracted from notify/$user + std::string credential_name; + + // Authorized accounts can edit the event list + std::set authorized_accounts; + + // *** User settings *** + + // Events to announce + std::set events; + + // Whether to announce events + bool enabled; + + auto to_toml() const -> toml::table; + static auto from_toml(const toml::table &v) -> ProjectSettings; +}; + +struct WebhookSettings { + std::string host; + std::string service; + + std::map credentials; + std::map projects; + + auto to_toml() const -> toml::table; + static auto from_toml(const toml::table &v) -> WebhookSettings; +}; + +struct WebhookEvent { + std::string channel; + std::string message; +}; + +class GithubWebhook { + // IRC connection to announce on; could be empty + std::shared_ptr connection_; + + // Buffered events in case connection was inactive when event was received + std::vector events_; + + + // Actually write the event to the connection. + // Only call when there is a connection. + auto write_event(WebhookEvent event) -> void; + +public: + WebhookSettings settings_; + + GithubWebhook(WebhookSettings settings) + : settings_(std::move(settings)) + { + } + + // Either emit the event now or save it until a connection is set + auto add_event(WebhookEvent event) -> void + { + if (connection_) { + write_event(std::move(event)); + } else { + events_.emplace_back(std::move(event)); + } + } + + auto set_connection(std::shared_ptr connection) -> void + { + connection_ = std::move(connection); + for (auto &&event : events_) + { + write_event(event); + } + events_.clear(); + } + + auto clear_connection() -> void + { + connection_.reset(); + } +}; + +auto start_webhook(boost::asio::io_context &io, const char *) -> std::shared_ptr; diff --git a/myirc/connection.cpp b/myirc/connection.cpp index 678aee9..437549f 100644 --- a/myirc/connection.cpp +++ b/myirc/connection.cpp @@ -8,15 +8,12 @@ #include #include -namespace { +namespace myirc { + #include "irc_commands.inc" using tcp_type = boost::asio::ip::tcp::socket; using tls_type = boost::asio::ssl::stream; -} // namespace - -namespace myirc { - using namespace std::literals; Connection::Connection(boost::asio::io_context &io) diff --git a/myirc/irc_commands.gperf b/myirc/irc_commands.gperf index 8257135..9cf741f 100644 --- a/myirc/irc_commands.gperf +++ b/myirc/irc_commands.gperf @@ -1,283 +1,283 @@ struct RecognizedCommand { char const* text; - myirc::IrcCommand command; + IrcCommand command; std::size_t min_args; std::size_t max_args; }; %% -001, myirc::IrcCommand::RPL_WELCOME, 2, 2 -002, myirc::IrcCommand::RPL_YOURHOST, 2, 2 -003, myirc::IrcCommand::RPL_CREATED, 2, 2 -004, myirc::IrcCommand::RPL_MYINFO, 5, 6 -005, myirc::IrcCommand::RPL_ISUPPORT, 2, 15 -008, myirc::IrcCommand::RPL_SNOMASK, 3, 3 -010, myirc::IrcCommand::RPL_REDIR, 4, 4 -015, myirc::IrcCommand::RPL_MAP, 2, 2 -017, myirc::IrcCommand::RPL_MAPEND, 2, 2 -043, myirc::IrcCommand::RPL_SAVENICK, 3, 3 -200, myirc::IrcCommand::RPL_TRACELINK, 5, 5 -201, myirc::IrcCommand::RPL_TRACECONNECTING, 4, 4 -202, myirc::IrcCommand::RPL_TRACEHANDSHAKE, 4, 4 -203, myirc::IrcCommand::RPL_TRACEUNKNOWN, 6, 6 -204, myirc::IrcCommand::RPL_TRACEOPERATOR, 7, 7 -205, myirc::IrcCommand::RPL_TRACEUSER, 7, 7 -206, myirc::IrcCommand::RPL_TRACESERVER, 8, 8 -208, myirc::IrcCommand::RPL_TRACENEWTYPE, 4, 4 -209, myirc::IrcCommand::RPL_TRACECLASS, 4, 4 -211, myirc::IrcCommand::RPL_STATSLINKINFO -212, myirc::IrcCommand::RPL_STATSCOMMANDS, 5, 5 -213, myirc::IrcCommand::RPL_STATSCLINE, 8, 8 -214, myirc::IrcCommand::RPL_STATSNLINE -215, myirc::IrcCommand::RPL_STATSILINE, 8, 8 -216, myirc::IrcCommand::RPL_STATSKLINE, 6, 6 -217, myirc::IrcCommand::RPL_STATSQLINE, 5, 5 -218, myirc::IrcCommand::RPL_STATSYLINE, 10, 10 -219, myirc::IrcCommand::RPL_ENDOFSTATS, 3, 3 -220, myirc::IrcCommand::RPL_STATSPLINE, 6, 6 -221, myirc::IrcCommand::RPL_UMODEIS, 2, 2 -224, myirc::IrcCommand::RPL_STATSFLINE -225, myirc::IrcCommand::RPL_STATSDLINE, 4, 4 -234, myirc::IrcCommand::RPL_SERVLIST -235, myirc::IrcCommand::RPL_SERVLISTEND -241, myirc::IrcCommand::RPL_STATSLLINE, 7, 7 -242, myirc::IrcCommand::RPL_STATSUPTIME, 2, 2 -243, myirc::IrcCommand::RPL_STATSOLINE, 7, 7 -244, myirc::IrcCommand::RPL_STATSHLINE, 7, 7 -245, myirc::IrcCommand::RPL_STATSSLINE -247, myirc::IrcCommand::RPL_STATSXLINE, 5, 5 -248, myirc::IrcCommand::RPL_STATSULINE, 5, 5 -249, myirc::IrcCommand::RPL_STATSDEBUG, 3, 3 -250, myirc::IrcCommand::RPL_STATSCONN, 2, 2 -251, myirc::IrcCommand::RPL_LUSERCLIENT, 2, 2 -252, myirc::IrcCommand::RPL_LUSEROP, 3, 3 -253, myirc::IrcCommand::RPL_LUSERUNKNOWN, 3, 3 -254, myirc::IrcCommand::RPL_LUSERCHANNELS, 3, 3 -255, myirc::IrcCommand::RPL_LUSERME, 2, 2 -256, myirc::IrcCommand::RPL_ADMINME, 3, 3 -257, myirc::IrcCommand::RPL_ADMINLOC1, 2, 2 -258, myirc::IrcCommand::RPL_ADMINLOC2, 2, 2 -259, myirc::IrcCommand::RPL_ADMINEMAIL, 2, 2 -261, myirc::IrcCommand::RPL_TRACELOG -262, myirc::IrcCommand::RPL_ENDOFTRACE, 3, 3 -263, myirc::IrcCommand::RPL_LOAD2HI, 3, 3 -265, myirc::IrcCommand::RPL_LOCALUSERS, 4, 4 -266, myirc::IrcCommand::RPL_GLOBALUSERS, 4, 4 -270, myirc::IrcCommand::RPL_PRIVS, 3, 3 -276, myirc::IrcCommand::RPL_WHOISCERTFP, 3, 3 -281, myirc::IrcCommand::RPL_ACCEPTLIST, 1, 15 -282, myirc::IrcCommand::RPL_ENDOFACCEPT, 2, 2 -300, myirc::IrcCommand::RPL_NONE -301, myirc::IrcCommand::RPL_AWAY, 3, 3 -302, myirc::IrcCommand::RPL_USERHOST, 2, 2 -303, myirc::IrcCommand::RPL_ISON, 2, 2 -304, myirc::IrcCommand::RPL_TEXT -305, myirc::IrcCommand::RPL_UNAWAY, 2, 2 -306, myirc::IrcCommand::RPL_NOWAWAY, 2, 2 -310, myirc::IrcCommand::RPL_WHOISHELPOP, 3, 3 -311, myirc::IrcCommand::RPL_WHOISUSER, 6, 6 -312, myirc::IrcCommand::RPL_WHOISSERVER, 4, 4 -313, myirc::IrcCommand::RPL_WHOISOPERATOR, 3, 3 -314, myirc::IrcCommand::RPL_WHOWASUSER, 6, 6 -369, myirc::IrcCommand::RPL_ENDOFWHOWAS, 3, 3 -316, myirc::IrcCommand::RPL_WHOISCHANOP -317, myirc::IrcCommand::RPL_WHOISIDLE, 5, 5 -318, myirc::IrcCommand::RPL_ENDOFWHOIS, 3, 3 -319, myirc::IrcCommand::RPL_WHOISCHANNELS, 3, 3 -320, myirc::IrcCommand::RPL_WHOISSPECIAL, 3, 3 -321, myirc::IrcCommand::RPL_LISTSTART, 3, 3 -322, myirc::IrcCommand::RPL_LIST, 4, 4 -323, myirc::IrcCommand::RPL_LISTEND, 2, 2 -324, myirc::IrcCommand::RPL_CHANNELMODEIS, 3, 3 -325, myirc::IrcCommand::RPL_CHANNELMLOCK, 4, 4 -328, myirc::IrcCommand::RPL_CHANNELURL -329, myirc::IrcCommand::RPL_CREATIONTIME, 3, 3 -330, myirc::IrcCommand::RPL_WHOISLOGGEDIN, 4, 4 -331, myirc::IrcCommand::RPL_NOTOPIC, 3, 3 -332, myirc::IrcCommand::RPL_TOPIC, 3, 3 -333, myirc::IrcCommand::RPL_TOPICWHOTIME, 4, 4 -338, myirc::IrcCommand::RPL_WHOISACTUALLY, 4, 4 -341, myirc::IrcCommand::RPL_INVITING, 3, 3 -342, myirc::IrcCommand::RPL_SUMMONING -346, myirc::IrcCommand::RPL_INVITELIST, 5, 5 -347, myirc::IrcCommand::RPL_ENDOFINVITELIST, 3, 3 -348, myirc::IrcCommand::RPL_EXCEPTLIST, 5, 5 -349, myirc::IrcCommand::RPL_ENDOFEXCEPTLIST, 3, 3 -351, myirc::IrcCommand::RPL_VERSION, 4, 4 -352, myirc::IrcCommand::RPL_WHOREPLY, 8, 8 -354, myirc::IrcCommand::RPL_WHOSPCRPL -315, myirc::IrcCommand::RPL_ENDOFWHO, 3, 3 -353, myirc::IrcCommand::RPL_NAMREPLY, 4, 4 -360, myirc::IrcCommand::RPL_WHOWASREAL, 3, 3 -366, myirc::IrcCommand::RPL_ENDOFNAMES, 3, 3 -361, myirc::IrcCommand::RPL_KILLDONE -362, myirc::IrcCommand::RPL_CLOSING, 3, 3 -363, myirc::IrcCommand::RPL_CLOSEEND, 3, 3 -364, myirc::IrcCommand::RPL_LINKS, 4, 4 -365, myirc::IrcCommand::RPL_ENDOFLINKS, 3, 3 -367, myirc::IrcCommand::RPL_BANLIST, 5, 5 -368, myirc::IrcCommand::RPL_ENDOFBANLIST, 3, 3 -371, myirc::IrcCommand::RPL_INFO, 2, 2 -372, myirc::IrcCommand::RPL_MOTD, 2, 2 -373, myirc::IrcCommand::RPL_INFOSTART -374, myirc::IrcCommand::RPL_ENDOFINFO, 2, 2 -375, myirc::IrcCommand::RPL_MOTDSTART, 2, 2 -376, myirc::IrcCommand::RPL_ENDOFMOTD, 2, 2 -378, myirc::IrcCommand::RPL_WHOISHOST, 3, 3 -381, myirc::IrcCommand::RPL_YOUREOPER, 2, 2 -382, myirc::IrcCommand::RPL_REHASHING, 3, 3 -384, myirc::IrcCommand::RPL_MYPORTIS -385, myirc::IrcCommand::RPL_NOTOPERANYMORE -386, myirc::IrcCommand::RPL_RSACHALLENGE, 2, 2 -391, myirc::IrcCommand::RPL_TIME, 3, 3 -392, myirc::IrcCommand::RPL_USERSSTART -393, myirc::IrcCommand::RPL_USERS -394, myirc::IrcCommand::RPL_ENDOFUSERS -395, myirc::IrcCommand::RPL_NOUSERS -396, myirc::IrcCommand::RPL_HOSTHIDDEN -401, myirc::IrcCommand::ERR_NOSUCHNICK, 3, 3 -402, myirc::IrcCommand::ERR_NOSUCHSERVER, 3, 3 -403, myirc::IrcCommand::ERR_NOSUCHCHANNEL, 3, 3 -404, myirc::IrcCommand::ERR_CANNOTSENDTOCHAN, 3, 3 -405, myirc::IrcCommand::ERR_TOOMANYCHANNELS, 3, 3 -406, myirc::IrcCommand::ERR_WASNOSUCHNICK, 3, 3 -407, myirc::IrcCommand::ERR_TOOMANYTARGETS, 3, 3 -409, myirc::IrcCommand::ERR_NOORIGIN, 2, 2 -410, myirc::IrcCommand::ERR_INVALIDCAPCMD, 3, 3 -411, myirc::IrcCommand::ERR_NORECIPIENT, 2, 2 -412, myirc::IrcCommand::ERR_NOTEXTTOSEND, 2, 2 -413, myirc::IrcCommand::ERR_NOTOPLEVEL, 3, 3 -414, myirc::IrcCommand::ERR_WILDTOPLEVEL, 3, 3 -415, myirc::IrcCommand::ERR_MSGNEEDREGGEDNICK, 3, 3 -416, myirc::IrcCommand::ERR_TOOMANYMATCHES, 3, 3 -421, myirc::IrcCommand::ERR_UNKNOWNCOMMAND, 3, 3 -422, myirc::IrcCommand::ERR_NOMOTD, 2, 2 -423, myirc::IrcCommand::ERR_NOADMININFO -424, myirc::IrcCommand::ERR_FILEERROR -431, myirc::IrcCommand::ERR_NONICKNAMEGIVEN, 2, 2 -432, myirc::IrcCommand::ERR_ERRONEUSNICKNAME, 3, 3 -433, myirc::IrcCommand::ERR_NICKNAMEINUSE, 3, 3 -435, myirc::IrcCommand::ERR_BANNICKCHANGE, 3, 3 -436, myirc::IrcCommand::ERR_NICKCOLLISION, 3, 3 -437, myirc::IrcCommand::ERR_UNAVAILRESOURCE, 3, 3 -438, myirc::IrcCommand::ERR_NICKTOOFAST, 4, 4 -440, myirc::IrcCommand::ERR_SERVICESDOWN, 3, 3 -441, myirc::IrcCommand::ERR_USERNOTINCHANNEL, 4, 4 -442, myirc::IrcCommand::ERR_NOTONCHANNEL, 3, 3 -443, myirc::IrcCommand::ERR_USERONCHANNEL, 4, 4 -444, myirc::IrcCommand::ERR_NOLOGIN -445, myirc::IrcCommand::ERR_SUMMONDISABLED -446, myirc::IrcCommand::ERR_USERSDISABLED -451, myirc::IrcCommand::ERR_NOTREGISTERED, 2, 2 -456, myirc::IrcCommand::ERR_ACCEPTFULL, 2, 2 -457, myirc::IrcCommand::ERR_ACCEPTEXIST, 3, 3 -458, myirc::IrcCommand::ERR_ACCEPTNOT, 3, 3 -461, myirc::IrcCommand::ERR_NEEDMOREPARAMS, 3, 3 -462, myirc::IrcCommand::ERR_ALREADYREGISTRED, 2, 2 -463, myirc::IrcCommand::ERR_NOPERMFORHOST -464, myirc::IrcCommand::ERR_PASSWDMISMATCH, 2, 2 -465, myirc::IrcCommand::ERR_YOUREBANNEDCREEP, 2, 2 -466, myirc::IrcCommand::ERR_YOUWILLBEBANNED -467, myirc::IrcCommand::ERR_KEYSET -470, myirc::IrcCommand::ERR_LINKCHANNEL, 4, 4 -471, myirc::IrcCommand::ERR_CHANNELISFULL, 3, 3 -472, myirc::IrcCommand::ERR_UNKNOWNMODE, 3, 3 -473, myirc::IrcCommand::ERR_INVITEONLYCHAN, 3, 3 -474, myirc::IrcCommand::ERR_BANNEDFROMCHAN, 3, 3 -475, myirc::IrcCommand::ERR_BADCHANNELKEY, 3, 3 -476, myirc::IrcCommand::ERR_BADCHANMASK -477, myirc::IrcCommand::ERR_NEEDREGGEDNICK, 3, 3 -478, myirc::IrcCommand::ERR_BANLISTFULL, 4, 4 -479, myirc::IrcCommand::ERR_BADCHANNAME, 3, 3 -480, myirc::IrcCommand::ERR_THROTTLE, 3, 3 -481, myirc::IrcCommand::ERR_NOPRIVILEGES, 2, 2 -482, myirc::IrcCommand::ERR_CHANOPRIVSNEEDED, 3, 3 -483, myirc::IrcCommand::ERR_CANTKILLSERVER, 2, 2 -484, myirc::IrcCommand::ERR_ISCHANSERVICE, 4, 4 -485, myirc::IrcCommand::ERR_BANNEDNICK -486, myirc::IrcCommand::ERR_NONONREG, 3, 3 -489, myirc::IrcCommand::ERR_VOICENEEDED, 3, 3 -491, myirc::IrcCommand::ERR_NOOPERHOST, 2, 2 -492, myirc::IrcCommand::ERR_CANNOTSENDTOUSER, 2, 2 -494, myirc::IrcCommand::ERR_OWNMODE, 3, 3 -501, myirc::IrcCommand::ERR_UMODEUNKNOWNFLAG, 2, 2 -502, myirc::IrcCommand::ERR_USERSDONTMATCH, 2, 2 -503, myirc::IrcCommand::ERR_GHOSTEDCLIENT -504, myirc::IrcCommand::ERR_USERNOTONSERV, 3, 3 -513, myirc::IrcCommand::ERR_WRONGPONG, 2, 2 -517, myirc::IrcCommand::ERR_DISABLED, 3, 3 -524, myirc::IrcCommand::ERR_HELPNOTFOUND, 3, 3 -670, myirc::IrcCommand::RPL_STARTTLS, 2, 2 -671, myirc::IrcCommand::RPL_WHOISSECURE, 3, 3 -691, myirc::IrcCommand::ERR_STARTTLS, 2, 2 -702, myirc::IrcCommand::RPL_MODLIST, 5, 5 -703, myirc::IrcCommand::RPL_ENDOFMODLIST, 2, 2 -704, myirc::IrcCommand::RPL_HELPSTART, 3, 3 -705, myirc::IrcCommand::RPL_HELPTXT, 3, 3 -706, myirc::IrcCommand::RPL_ENDOFHELP, 3, 3 -707, myirc::IrcCommand::ERR_TARGCHANGE, 3, 3 -708, myirc::IrcCommand::RPL_ETRACEFULL, 10, 10 -709, myirc::IrcCommand::RPL_ETRACE, 8, 8 -710, myirc::IrcCommand::RPL_KNOCK, 4, 4 -711, myirc::IrcCommand::RPL_KNOCKDLVR, 3, 3 -712, myirc::IrcCommand::ERR_TOOMANYKNOCK, 3, 3 -713, myirc::IrcCommand::ERR_CHANOPEN, 3, 3 -714, myirc::IrcCommand::ERR_KNOCKONCHAN, 3, 3 -715, myirc::IrcCommand::ERR_KNOCKDISABLED, 2, 2 -716, myirc::IrcCommand::ERR_TARGUMODEG, 3, 3 -717, myirc::IrcCommand::RPL_TARGNOTIFY -718, myirc::IrcCommand::RPL_UMODEGMSG, 4, 4 -720, myirc::IrcCommand::RPL_OMOTDSTART, 2, 2 -721, myirc::IrcCommand::RPL_OMOTD, 2, 2 -722, myirc::IrcCommand::RPL_ENDOFOMOTD, 2, 2 -723, myirc::IrcCommand::ERR_NOPRIVS, 3, 3 -724, myirc::IrcCommand::RPL_TESTMASK -725, myirc::IrcCommand::RPL_TESTLINE, 5, 5 -726, myirc::IrcCommand::RPL_NOTESTLINE -727, myirc::IrcCommand::RPL_TESTMASKGECO, 6, 6 -728, myirc::IrcCommand::RPL_QUIETLIST, 6, 6 -729, myirc::IrcCommand::RPL_ENDOFQUIETLIS, 4, 4 -730, myirc::IrcCommand::RPL_MONONLINE, 2, 2 -731, myirc::IrcCommand::RPL_MONOFFLINE, 2, 2 -732, myirc::IrcCommand::RPL_MONLIST, 2, 2 -733, myirc::IrcCommand::RPL_ENDOFMONLIS, 2, 2 -734, myirc::IrcCommand::ERR_MONLISTFULL, 4, 4 -740, myirc::IrcCommand::RPL_RSACHALLENGE2, 2, 2 -741, myirc::IrcCommand::RPL_ENDOFRSACHALLENGE2, 2, 2 -742, myirc::IrcCommand::ERR_MLOCKRESTRICTE, 5, 5 -743, myirc::IrcCommand::ERR_INVALIDBAN, 5, 5 -744, myirc::IrcCommand::ERR_TOPICLOCK -750, myirc::IrcCommand::RPL_SCANMATCHED, 3, 3 -751, myirc::IrcCommand::RPL_SCANUMODES, 8, 8 -900, myirc::IrcCommand::RPL_LOGGEDIN, 4, 4 -901, myirc::IrcCommand::RPL_LOGGEDOUT, 3, 3 -902, myirc::IrcCommand::ERR_NICKLOCKED, 2, 2 -903, myirc::IrcCommand::RPL_SASLSUCCESS, 2, 2 -904, myirc::IrcCommand::ERR_SASLFAIL, 2, 2 -905, myirc::IrcCommand::ERR_SASLTOOLONG, 2, 2 -906, myirc::IrcCommand::ERR_SASLABORTED, 2, 2 -907, myirc::IrcCommand::ERR_SASLALREADY, 2, 2 -908, myirc::IrcCommand::RPL_SASLMECHS, 3, 3 -ACCOUNT, myirc::IrcCommand::ACCOUNT, 1, 1 -AUTHENTICATE, myirc::IrcCommand::AUTHENTICATE, 1, 1 -AWAY, myirc::IrcCommand::AWAY, 0, 1 -BATCH, myirc::IrcCommand::BATCH -BOUNCER, myirc::IrcCommand::BOUNCER -CAP, myirc::IrcCommand::CAP, 1, 15 -CHGHOST, myirc::IrcCommand::CHGHOST, 2, 2 -ERROR, myirc::IrcCommand::ERROR, 1, 1 -INVITE, myirc::IrcCommand::INVITE, 2, 2 -JOIN, myirc::IrcCommand::JOIN, 1, 3 -KICK, myirc::IrcCommand::KICK, 3, 3 -KILL, myirc::IrcCommand::KILL, 2, 2 -MODE, myirc::IrcCommand::MODE, 2, 15 -NICK, myirc::IrcCommand::NICK, 1, 1 -NOTICE, myirc::IrcCommand::NOTICE, 2, 2 -PART, myirc::IrcCommand::PART, 1, 2 -PING, myirc::IrcCommand::PING, 1, 1 -PONG, myirc::IrcCommand::PONG, 1, 2 -PRIVMSG, myirc::IrcCommand::PRIVMSG, 2, 2 -QUIT, myirc::IrcCommand::QUIT, 1, 1 -SETNAME, myirc::IrcCommand::SETNAME, 1, 1 -TAGMSG, myirc::IrcCommand::TAGMSG, 1, 1 -TOPIC, myirc::IrcCommand::TOPIC, 2, 2 -WALLOPS, myirc::IrcCommand::WALLOPS, 1, 1 +001, IrcCommand::RPL_WELCOME, 2, 2 +002, IrcCommand::RPL_YOURHOST, 2, 2 +003, IrcCommand::RPL_CREATED, 2, 2 +004, IrcCommand::RPL_MYINFO, 5, 6 +005, IrcCommand::RPL_ISUPPORT, 2, 15 +008, IrcCommand::RPL_SNOMASK, 3, 3 +010, IrcCommand::RPL_REDIR, 4, 4 +015, IrcCommand::RPL_MAP, 2, 2 +017, IrcCommand::RPL_MAPEND, 2, 2 +043, IrcCommand::RPL_SAVENICK, 3, 3 +200, IrcCommand::RPL_TRACELINK, 5, 5 +201, IrcCommand::RPL_TRACECONNECTING, 4, 4 +202, IrcCommand::RPL_TRACEHANDSHAKE, 4, 4 +203, IrcCommand::RPL_TRACEUNKNOWN, 6, 6 +204, IrcCommand::RPL_TRACEOPERATOR, 7, 7 +205, IrcCommand::RPL_TRACEUSER, 7, 7 +206, IrcCommand::RPL_TRACESERVER, 8, 8 +208, IrcCommand::RPL_TRACENEWTYPE, 4, 4 +209, IrcCommand::RPL_TRACECLASS, 4, 4 +211, IrcCommand::RPL_STATSLINKINFO +212, IrcCommand::RPL_STATSCOMMANDS, 5, 5 +213, IrcCommand::RPL_STATSCLINE, 8, 8 +214, IrcCommand::RPL_STATSNLINE +215, IrcCommand::RPL_STATSILINE, 8, 8 +216, IrcCommand::RPL_STATSKLINE, 6, 6 +217, IrcCommand::RPL_STATSQLINE, 5, 5 +218, IrcCommand::RPL_STATSYLINE, 10, 10 +219, IrcCommand::RPL_ENDOFSTATS, 3, 3 +220, IrcCommand::RPL_STATSPLINE, 6, 6 +221, IrcCommand::RPL_UMODEIS, 2, 2 +224, IrcCommand::RPL_STATSFLINE +225, IrcCommand::RPL_STATSDLINE, 4, 4 +234, IrcCommand::RPL_SERVLIST +235, IrcCommand::RPL_SERVLISTEND +241, IrcCommand::RPL_STATSLLINE, 7, 7 +242, IrcCommand::RPL_STATSUPTIME, 2, 2 +243, IrcCommand::RPL_STATSOLINE, 7, 7 +244, IrcCommand::RPL_STATSHLINE, 7, 7 +245, IrcCommand::RPL_STATSSLINE +247, IrcCommand::RPL_STATSXLINE, 5, 5 +248, IrcCommand::RPL_STATSULINE, 5, 5 +249, IrcCommand::RPL_STATSDEBUG, 3, 3 +250, IrcCommand::RPL_STATSCONN, 2, 2 +251, IrcCommand::RPL_LUSERCLIENT, 2, 2 +252, IrcCommand::RPL_LUSEROP, 3, 3 +253, IrcCommand::RPL_LUSERUNKNOWN, 3, 3 +254, IrcCommand::RPL_LUSERCHANNELS, 3, 3 +255, IrcCommand::RPL_LUSERME, 2, 2 +256, IrcCommand::RPL_ADMINME, 3, 3 +257, IrcCommand::RPL_ADMINLOC1, 2, 2 +258, IrcCommand::RPL_ADMINLOC2, 2, 2 +259, IrcCommand::RPL_ADMINEMAIL, 2, 2 +261, IrcCommand::RPL_TRACELOG +262, IrcCommand::RPL_ENDOFTRACE, 3, 3 +263, IrcCommand::RPL_LOAD2HI, 3, 3 +265, IrcCommand::RPL_LOCALUSERS, 4, 4 +266, IrcCommand::RPL_GLOBALUSERS, 4, 4 +270, IrcCommand::RPL_PRIVS, 3, 3 +276, IrcCommand::RPL_WHOISCERTFP, 3, 3 +281, IrcCommand::RPL_ACCEPTLIST, 1, 15 +282, IrcCommand::RPL_ENDOFACCEPT, 2, 2 +300, IrcCommand::RPL_NONE +301, IrcCommand::RPL_AWAY, 3, 3 +302, IrcCommand::RPL_USERHOST, 2, 2 +303, IrcCommand::RPL_ISON, 2, 2 +304, IrcCommand::RPL_TEXT +305, IrcCommand::RPL_UNAWAY, 2, 2 +306, IrcCommand::RPL_NOWAWAY, 2, 2 +310, IrcCommand::RPL_WHOISHELPOP, 3, 3 +311, IrcCommand::RPL_WHOISUSER, 6, 6 +312, IrcCommand::RPL_WHOISSERVER, 4, 4 +313, IrcCommand::RPL_WHOISOPERATOR, 3, 3 +314, IrcCommand::RPL_WHOWASUSER, 6, 6 +369, IrcCommand::RPL_ENDOFWHOWAS, 3, 3 +316, IrcCommand::RPL_WHOISCHANOP +317, IrcCommand::RPL_WHOISIDLE, 5, 5 +318, IrcCommand::RPL_ENDOFWHOIS, 3, 3 +319, IrcCommand::RPL_WHOISCHANNELS, 3, 3 +320, IrcCommand::RPL_WHOISSPECIAL, 3, 3 +321, IrcCommand::RPL_LISTSTART, 3, 3 +322, IrcCommand::RPL_LIST, 4, 4 +323, IrcCommand::RPL_LISTEND, 2, 2 +324, IrcCommand::RPL_CHANNELMODEIS, 3, 3 +325, IrcCommand::RPL_CHANNELMLOCK, 4, 4 +328, IrcCommand::RPL_CHANNELURL +329, IrcCommand::RPL_CREATIONTIME, 3, 3 +330, IrcCommand::RPL_WHOISLOGGEDIN, 4, 4 +331, IrcCommand::RPL_NOTOPIC, 3, 3 +332, IrcCommand::RPL_TOPIC, 3, 3 +333, IrcCommand::RPL_TOPICWHOTIME, 4, 4 +338, IrcCommand::RPL_WHOISACTUALLY, 4, 4 +341, IrcCommand::RPL_INVITING, 3, 3 +342, IrcCommand::RPL_SUMMONING +346, IrcCommand::RPL_INVITELIST, 5, 5 +347, IrcCommand::RPL_ENDOFINVITELIST, 3, 3 +348, IrcCommand::RPL_EXCEPTLIST, 5, 5 +349, IrcCommand::RPL_ENDOFEXCEPTLIST, 3, 3 +351, IrcCommand::RPL_VERSION, 4, 4 +352, IrcCommand::RPL_WHOREPLY, 8, 8 +354, IrcCommand::RPL_WHOSPCRPL +315, IrcCommand::RPL_ENDOFWHO, 3, 3 +353, IrcCommand::RPL_NAMREPLY, 4, 4 +360, IrcCommand::RPL_WHOWASREAL, 3, 3 +366, IrcCommand::RPL_ENDOFNAMES, 3, 3 +361, IrcCommand::RPL_KILLDONE +362, IrcCommand::RPL_CLOSING, 3, 3 +363, IrcCommand::RPL_CLOSEEND, 3, 3 +364, IrcCommand::RPL_LINKS, 4, 4 +365, IrcCommand::RPL_ENDOFLINKS, 3, 3 +367, IrcCommand::RPL_BANLIST, 5, 5 +368, IrcCommand::RPL_ENDOFBANLIST, 3, 3 +371, IrcCommand::RPL_INFO, 2, 2 +372, IrcCommand::RPL_MOTD, 2, 2 +373, IrcCommand::RPL_INFOSTART +374, IrcCommand::RPL_ENDOFINFO, 2, 2 +375, IrcCommand::RPL_MOTDSTART, 2, 2 +376, IrcCommand::RPL_ENDOFMOTD, 2, 2 +378, IrcCommand::RPL_WHOISHOST, 3, 3 +381, IrcCommand::RPL_YOUREOPER, 2, 2 +382, IrcCommand::RPL_REHASHING, 3, 3 +384, IrcCommand::RPL_MYPORTIS +385, IrcCommand::RPL_NOTOPERANYMORE +386, IrcCommand::RPL_RSACHALLENGE, 2, 2 +391, IrcCommand::RPL_TIME, 3, 3 +392, IrcCommand::RPL_USERSSTART +393, IrcCommand::RPL_USERS +394, IrcCommand::RPL_ENDOFUSERS +395, IrcCommand::RPL_NOUSERS +396, IrcCommand::RPL_HOSTHIDDEN +401, IrcCommand::ERR_NOSUCHNICK, 3, 3 +402, IrcCommand::ERR_NOSUCHSERVER, 3, 3 +403, IrcCommand::ERR_NOSUCHCHANNEL, 3, 3 +404, IrcCommand::ERR_CANNOTSENDTOCHAN, 3, 3 +405, IrcCommand::ERR_TOOMANYCHANNELS, 3, 3 +406, IrcCommand::ERR_WASNOSUCHNICK, 3, 3 +407, IrcCommand::ERR_TOOMANYTARGETS, 3, 3 +409, IrcCommand::ERR_NOORIGIN, 2, 2 +410, IrcCommand::ERR_INVALIDCAPCMD, 3, 3 +411, IrcCommand::ERR_NORECIPIENT, 2, 2 +412, IrcCommand::ERR_NOTEXTTOSEND, 2, 2 +413, IrcCommand::ERR_NOTOPLEVEL, 3, 3 +414, IrcCommand::ERR_WILDTOPLEVEL, 3, 3 +415, IrcCommand::ERR_MSGNEEDREGGEDNICK, 3, 3 +416, IrcCommand::ERR_TOOMANYMATCHES, 3, 3 +421, IrcCommand::ERR_UNKNOWNCOMMAND, 3, 3 +422, IrcCommand::ERR_NOMOTD, 2, 2 +423, IrcCommand::ERR_NOADMININFO +424, IrcCommand::ERR_FILEERROR +431, IrcCommand::ERR_NONICKNAMEGIVEN, 2, 2 +432, IrcCommand::ERR_ERRONEUSNICKNAME, 3, 3 +433, IrcCommand::ERR_NICKNAMEINUSE, 3, 3 +435, IrcCommand::ERR_BANNICKCHANGE, 3, 3 +436, IrcCommand::ERR_NICKCOLLISION, 3, 3 +437, IrcCommand::ERR_UNAVAILRESOURCE, 3, 3 +438, IrcCommand::ERR_NICKTOOFAST, 4, 4 +440, IrcCommand::ERR_SERVICESDOWN, 3, 3 +441, IrcCommand::ERR_USERNOTINCHANNEL, 4, 4 +442, IrcCommand::ERR_NOTONCHANNEL, 3, 3 +443, IrcCommand::ERR_USERONCHANNEL, 4, 4 +444, IrcCommand::ERR_NOLOGIN +445, IrcCommand::ERR_SUMMONDISABLED +446, IrcCommand::ERR_USERSDISABLED +451, IrcCommand::ERR_NOTREGISTERED, 2, 2 +456, IrcCommand::ERR_ACCEPTFULL, 2, 2 +457, IrcCommand::ERR_ACCEPTEXIST, 3, 3 +458, IrcCommand::ERR_ACCEPTNOT, 3, 3 +461, IrcCommand::ERR_NEEDMOREPARAMS, 3, 3 +462, IrcCommand::ERR_ALREADYREGISTRED, 2, 2 +463, IrcCommand::ERR_NOPERMFORHOST +464, IrcCommand::ERR_PASSWDMISMATCH, 2, 2 +465, IrcCommand::ERR_YOUREBANNEDCREEP, 2, 2 +466, IrcCommand::ERR_YOUWILLBEBANNED +467, IrcCommand::ERR_KEYSET +470, IrcCommand::ERR_LINKCHANNEL, 4, 4 +471, IrcCommand::ERR_CHANNELISFULL, 3, 3 +472, IrcCommand::ERR_UNKNOWNMODE, 3, 3 +473, IrcCommand::ERR_INVITEONLYCHAN, 3, 3 +474, IrcCommand::ERR_BANNEDFROMCHAN, 3, 3 +475, IrcCommand::ERR_BADCHANNELKEY, 3, 3 +476, IrcCommand::ERR_BADCHANMASK +477, IrcCommand::ERR_NEEDREGGEDNICK, 3, 3 +478, IrcCommand::ERR_BANLISTFULL, 4, 4 +479, IrcCommand::ERR_BADCHANNAME, 3, 3 +480, IrcCommand::ERR_THROTTLE, 3, 3 +481, IrcCommand::ERR_NOPRIVILEGES, 2, 2 +482, IrcCommand::ERR_CHANOPRIVSNEEDED, 3, 3 +483, IrcCommand::ERR_CANTKILLSERVER, 2, 2 +484, IrcCommand::ERR_ISCHANSERVICE, 4, 4 +485, IrcCommand::ERR_BANNEDNICK +486, IrcCommand::ERR_NONONREG, 3, 3 +489, IrcCommand::ERR_VOICENEEDED, 3, 3 +491, IrcCommand::ERR_NOOPERHOST, 2, 2 +492, IrcCommand::ERR_CANNOTSENDTOUSER, 2, 2 +494, IrcCommand::ERR_OWNMODE, 3, 3 +501, IrcCommand::ERR_UMODEUNKNOWNFLAG, 2, 2 +502, IrcCommand::ERR_USERSDONTMATCH, 2, 2 +503, IrcCommand::ERR_GHOSTEDCLIENT +504, IrcCommand::ERR_USERNOTONSERV, 3, 3 +513, IrcCommand::ERR_WRONGPONG, 2, 2 +517, IrcCommand::ERR_DISABLED, 3, 3 +524, IrcCommand::ERR_HELPNOTFOUND, 3, 3 +670, IrcCommand::RPL_STARTTLS, 2, 2 +671, IrcCommand::RPL_WHOISSECURE, 3, 3 +691, IrcCommand::ERR_STARTTLS, 2, 2 +702, IrcCommand::RPL_MODLIST, 5, 5 +703, IrcCommand::RPL_ENDOFMODLIST, 2, 2 +704, IrcCommand::RPL_HELPSTART, 3, 3 +705, IrcCommand::RPL_HELPTXT, 3, 3 +706, IrcCommand::RPL_ENDOFHELP, 3, 3 +707, IrcCommand::ERR_TARGCHANGE, 3, 3 +708, IrcCommand::RPL_ETRACEFULL, 10, 10 +709, IrcCommand::RPL_ETRACE, 8, 8 +710, IrcCommand::RPL_KNOCK, 4, 4 +711, IrcCommand::RPL_KNOCKDLVR, 3, 3 +712, IrcCommand::ERR_TOOMANYKNOCK, 3, 3 +713, IrcCommand::ERR_CHANOPEN, 3, 3 +714, IrcCommand::ERR_KNOCKONCHAN, 3, 3 +715, IrcCommand::ERR_KNOCKDISABLED, 2, 2 +716, IrcCommand::ERR_TARGUMODEG, 3, 3 +717, IrcCommand::RPL_TARGNOTIFY +718, IrcCommand::RPL_UMODEGMSG, 4, 4 +720, IrcCommand::RPL_OMOTDSTART, 2, 2 +721, IrcCommand::RPL_OMOTD, 2, 2 +722, IrcCommand::RPL_ENDOFOMOTD, 2, 2 +723, IrcCommand::ERR_NOPRIVS, 3, 3 +724, IrcCommand::RPL_TESTMASK +725, IrcCommand::RPL_TESTLINE, 5, 5 +726, IrcCommand::RPL_NOTESTLINE +727, IrcCommand::RPL_TESTMASKGECO, 6, 6 +728, IrcCommand::RPL_QUIETLIST, 6, 6 +729, IrcCommand::RPL_ENDOFQUIETLIS, 4, 4 +730, IrcCommand::RPL_MONONLINE, 2, 2 +731, IrcCommand::RPL_MONOFFLINE, 2, 2 +732, IrcCommand::RPL_MONLIST, 2, 2 +733, IrcCommand::RPL_ENDOFMONLIS, 2, 2 +734, IrcCommand::ERR_MONLISTFULL, 4, 4 +740, IrcCommand::RPL_RSACHALLENGE2, 2, 2 +741, IrcCommand::RPL_ENDOFRSACHALLENGE2, 2, 2 +742, IrcCommand::ERR_MLOCKRESTRICTE, 5, 5 +743, IrcCommand::ERR_INVALIDBAN, 5, 5 +744, IrcCommand::ERR_TOPICLOCK +750, IrcCommand::RPL_SCANMATCHED, 3, 3 +751, IrcCommand::RPL_SCANUMODES, 8, 8 +900, IrcCommand::RPL_LOGGEDIN, 4, 4 +901, IrcCommand::RPL_LOGGEDOUT, 3, 3 +902, IrcCommand::ERR_NICKLOCKED, 2, 2 +903, IrcCommand::RPL_SASLSUCCESS, 2, 2 +904, IrcCommand::ERR_SASLFAIL, 2, 2 +905, IrcCommand::ERR_SASLTOOLONG, 2, 2 +906, IrcCommand::ERR_SASLABORTED, 2, 2 +907, IrcCommand::ERR_SASLALREADY, 2, 2 +908, IrcCommand::RPL_SASLMECHS, 3, 3 +ACCOUNT, IrcCommand::ACCOUNT, 1, 1 +AUTHENTICATE, IrcCommand::AUTHENTICATE, 1, 1 +AWAY, IrcCommand::AWAY, 0, 1 +BATCH, IrcCommand::BATCH +BOUNCER, IrcCommand::BOUNCER +CAP, IrcCommand::CAP, 1, 15 +CHGHOST, IrcCommand::CHGHOST, 2, 2 +ERROR, IrcCommand::ERROR, 1, 1 +INVITE, IrcCommand::INVITE, 2, 2 +JOIN, IrcCommand::JOIN, 1, 3 +KICK, IrcCommand::KICK, 3, 3 +KILL, IrcCommand::KILL, 2, 2 +MODE, IrcCommand::MODE, 2, 15 +NICK, IrcCommand::NICK, 1, 1 +NOTICE, IrcCommand::NOTICE, 2, 2 +PART, IrcCommand::PART, 1, 2 +PING, IrcCommand::PING, 1, 1 +PONG, IrcCommand::PONG, 1, 2 +PRIVMSG, IrcCommand::PRIVMSG, 2, 2 +QUIT, IrcCommand::QUIT, 1, 1 +SETNAME, IrcCommand::SETNAME, 1, 1 +TAGMSG, IrcCommand::TAGMSG, 1, 1 +TOPIC, IrcCommand::TOPIC, 2, 2 +WALLOPS, IrcCommand::WALLOPS, 1, 1