initial webhook infrastructure

This commit is contained in:
Eric Mertens 2025-02-01 20:57:57 -08:00
parent 1a6ec835ed
commit 4c119c6138
5 changed files with 657 additions and 287 deletions

View File

@ -60,7 +60,11 @@ auto configure_sasl(const Settings &settings) -> std::unique_ptr<SaslMechanism>
return nullptr; 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<GithubWebhook> webhook
) -> void
{ {
Ref<X509> tls_cert; Ref<X509> tls_cert;
if (settings.use_tls && not settings.tls_cert_file.empty()) 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 // On disconnect reconnect in 5 seconds
// connection is captured in the disconnect handler so it can keep itself alive // connection is captured in the disconnect handler so it can keep itself alive
connection->sig_disconnect.connect( connection->sig_disconnect.connect(
[&io, &settings, connection]() { [&io, &settings, connection, webhook]() {
webhook->clear_connection();
auto timer = std::make_shared<boost::asio::steady_timer>(io); auto timer = std::make_shared<boost::asio::steady_timer>(io);
timer->expires_after(5s); 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); //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"; BOOST_LOG_TRIVIAL(error) << "Bad arguments";
return 1; return 1;
} }
const auto settings = get_settings(argv[1]); const auto settings = get_settings(argv[1]);
auto io = boost::asio::io_context{}; auto io = boost::asio::io_context{};
start(io, settings);
auto webhooks = start_webhook(io, argv[2]);
start(io, settings, webhooks);
io.run(); io.run();
} }

View File

@ -1 +1,263 @@
#include "web.hpp" #include "web.hpp"
#include <boost/beast.hpp>
#include <boost/log/trivial.hpp>
#include <chrono>
#include <fstream>
#include <vector>
namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
namespace net = boost::asio; // from <boost/asio.hpp>
namespace websocket = beast::websocket;
using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp>
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 <class Body, class Allocator>
auto handle_request(
std::shared_ptr<GithubWebhook> self,
http::request<Body, http::basic_fields<Allocator>> &&req
) -> http::message_generator
{
self->add_event({"project", "message"});
http::response<http::string_body> 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<GithubWebhook> self) -> boost::asio::awaitable<void>
{
beast::tcp_stream stream{std::move(socket)};
beast::flat_buffer buffer;
http::request<http::string_body> 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<GithubWebhook> self
) -> boost::asio::awaitable<void>
{
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<GithubWebhook> webhook
) -> boost::asio::awaitable<void>
{
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<GithubWebhook>
{
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<GithubWebhook>(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)}
};
}

View File

@ -1 +1,100 @@
#pragma once #pragma once
#include <myirc/connection.hpp>
#include <toml++/toml.hpp>
#include <boost/asio.hpp>
#include <boost/signals2.hpp>
#include <memory>
#include <map>
#include <set>
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<std::string> authorized_accounts;
// *** User settings ***
// Events to announce
std::set<std::string> 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<std::string, std::string> credentials;
std::map<std::string, ProjectSettings> 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<myirc::Connection> connection_;
// Buffered events in case connection was inactive when event was received
std::vector<WebhookEvent> 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<myirc::Connection> 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<GithubWebhook>;

View File

@ -8,15 +8,12 @@
#include <boost/asio/steady_timer.hpp> #include <boost/asio/steady_timer.hpp>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
namespace { namespace myirc {
#include "irc_commands.inc" #include "irc_commands.inc"
using tcp_type = boost::asio::ip::tcp::socket; using tcp_type = boost::asio::ip::tcp::socket;
using tls_type = boost::asio::ssl::stream<tcp_type>; using tls_type = boost::asio::ssl::stream<tcp_type>;
} // namespace
namespace myirc {
using namespace std::literals; using namespace std::literals;
Connection::Connection(boost::asio::io_context &io) Connection::Connection(boost::asio::io_context &io)

View File

@ -1,283 +1,283 @@
struct RecognizedCommand { struct RecognizedCommand {
char const* text; char const* text;
myirc::IrcCommand command; IrcCommand command;
std::size_t min_args; std::size_t min_args;
std::size_t max_args; std::size_t max_args;
}; };
%% %%
001, myirc::IrcCommand::RPL_WELCOME, 2, 2 001, IrcCommand::RPL_WELCOME, 2, 2
002, myirc::IrcCommand::RPL_YOURHOST, 2, 2 002, IrcCommand::RPL_YOURHOST, 2, 2
003, myirc::IrcCommand::RPL_CREATED, 2, 2 003, IrcCommand::RPL_CREATED, 2, 2
004, myirc::IrcCommand::RPL_MYINFO, 5, 6 004, IrcCommand::RPL_MYINFO, 5, 6
005, myirc::IrcCommand::RPL_ISUPPORT, 2, 15 005, IrcCommand::RPL_ISUPPORT, 2, 15
008, myirc::IrcCommand::RPL_SNOMASK, 3, 3 008, IrcCommand::RPL_SNOMASK, 3, 3
010, myirc::IrcCommand::RPL_REDIR, 4, 4 010, IrcCommand::RPL_REDIR, 4, 4
015, myirc::IrcCommand::RPL_MAP, 2, 2 015, IrcCommand::RPL_MAP, 2, 2
017, myirc::IrcCommand::RPL_MAPEND, 2, 2 017, IrcCommand::RPL_MAPEND, 2, 2
043, myirc::IrcCommand::RPL_SAVENICK, 3, 3 043, IrcCommand::RPL_SAVENICK, 3, 3
200, myirc::IrcCommand::RPL_TRACELINK, 5, 5 200, IrcCommand::RPL_TRACELINK, 5, 5
201, myirc::IrcCommand::RPL_TRACECONNECTING, 4, 4 201, IrcCommand::RPL_TRACECONNECTING, 4, 4
202, myirc::IrcCommand::RPL_TRACEHANDSHAKE, 4, 4 202, IrcCommand::RPL_TRACEHANDSHAKE, 4, 4
203, myirc::IrcCommand::RPL_TRACEUNKNOWN, 6, 6 203, IrcCommand::RPL_TRACEUNKNOWN, 6, 6
204, myirc::IrcCommand::RPL_TRACEOPERATOR, 7, 7 204, IrcCommand::RPL_TRACEOPERATOR, 7, 7
205, myirc::IrcCommand::RPL_TRACEUSER, 7, 7 205, IrcCommand::RPL_TRACEUSER, 7, 7
206, myirc::IrcCommand::RPL_TRACESERVER, 8, 8 206, IrcCommand::RPL_TRACESERVER, 8, 8
208, myirc::IrcCommand::RPL_TRACENEWTYPE, 4, 4 208, IrcCommand::RPL_TRACENEWTYPE, 4, 4
209, myirc::IrcCommand::RPL_TRACECLASS, 4, 4 209, IrcCommand::RPL_TRACECLASS, 4, 4
211, myirc::IrcCommand::RPL_STATSLINKINFO 211, IrcCommand::RPL_STATSLINKINFO
212, myirc::IrcCommand::RPL_STATSCOMMANDS, 5, 5 212, IrcCommand::RPL_STATSCOMMANDS, 5, 5
213, myirc::IrcCommand::RPL_STATSCLINE, 8, 8 213, IrcCommand::RPL_STATSCLINE, 8, 8
214, myirc::IrcCommand::RPL_STATSNLINE 214, IrcCommand::RPL_STATSNLINE
215, myirc::IrcCommand::RPL_STATSILINE, 8, 8 215, IrcCommand::RPL_STATSILINE, 8, 8
216, myirc::IrcCommand::RPL_STATSKLINE, 6, 6 216, IrcCommand::RPL_STATSKLINE, 6, 6
217, myirc::IrcCommand::RPL_STATSQLINE, 5, 5 217, IrcCommand::RPL_STATSQLINE, 5, 5
218, myirc::IrcCommand::RPL_STATSYLINE, 10, 10 218, IrcCommand::RPL_STATSYLINE, 10, 10
219, myirc::IrcCommand::RPL_ENDOFSTATS, 3, 3 219, IrcCommand::RPL_ENDOFSTATS, 3, 3
220, myirc::IrcCommand::RPL_STATSPLINE, 6, 6 220, IrcCommand::RPL_STATSPLINE, 6, 6
221, myirc::IrcCommand::RPL_UMODEIS, 2, 2 221, IrcCommand::RPL_UMODEIS, 2, 2
224, myirc::IrcCommand::RPL_STATSFLINE 224, IrcCommand::RPL_STATSFLINE
225, myirc::IrcCommand::RPL_STATSDLINE, 4, 4 225, IrcCommand::RPL_STATSDLINE, 4, 4
234, myirc::IrcCommand::RPL_SERVLIST 234, IrcCommand::RPL_SERVLIST
235, myirc::IrcCommand::RPL_SERVLISTEND 235, IrcCommand::RPL_SERVLISTEND
241, myirc::IrcCommand::RPL_STATSLLINE, 7, 7 241, IrcCommand::RPL_STATSLLINE, 7, 7
242, myirc::IrcCommand::RPL_STATSUPTIME, 2, 2 242, IrcCommand::RPL_STATSUPTIME, 2, 2
243, myirc::IrcCommand::RPL_STATSOLINE, 7, 7 243, IrcCommand::RPL_STATSOLINE, 7, 7
244, myirc::IrcCommand::RPL_STATSHLINE, 7, 7 244, IrcCommand::RPL_STATSHLINE, 7, 7
245, myirc::IrcCommand::RPL_STATSSLINE 245, IrcCommand::RPL_STATSSLINE
247, myirc::IrcCommand::RPL_STATSXLINE, 5, 5 247, IrcCommand::RPL_STATSXLINE, 5, 5
248, myirc::IrcCommand::RPL_STATSULINE, 5, 5 248, IrcCommand::RPL_STATSULINE, 5, 5
249, myirc::IrcCommand::RPL_STATSDEBUG, 3, 3 249, IrcCommand::RPL_STATSDEBUG, 3, 3
250, myirc::IrcCommand::RPL_STATSCONN, 2, 2 250, IrcCommand::RPL_STATSCONN, 2, 2
251, myirc::IrcCommand::RPL_LUSERCLIENT, 2, 2 251, IrcCommand::RPL_LUSERCLIENT, 2, 2
252, myirc::IrcCommand::RPL_LUSEROP, 3, 3 252, IrcCommand::RPL_LUSEROP, 3, 3
253, myirc::IrcCommand::RPL_LUSERUNKNOWN, 3, 3 253, IrcCommand::RPL_LUSERUNKNOWN, 3, 3
254, myirc::IrcCommand::RPL_LUSERCHANNELS, 3, 3 254, IrcCommand::RPL_LUSERCHANNELS, 3, 3
255, myirc::IrcCommand::RPL_LUSERME, 2, 2 255, IrcCommand::RPL_LUSERME, 2, 2
256, myirc::IrcCommand::RPL_ADMINME, 3, 3 256, IrcCommand::RPL_ADMINME, 3, 3
257, myirc::IrcCommand::RPL_ADMINLOC1, 2, 2 257, IrcCommand::RPL_ADMINLOC1, 2, 2
258, myirc::IrcCommand::RPL_ADMINLOC2, 2, 2 258, IrcCommand::RPL_ADMINLOC2, 2, 2
259, myirc::IrcCommand::RPL_ADMINEMAIL, 2, 2 259, IrcCommand::RPL_ADMINEMAIL, 2, 2
261, myirc::IrcCommand::RPL_TRACELOG 261, IrcCommand::RPL_TRACELOG
262, myirc::IrcCommand::RPL_ENDOFTRACE, 3, 3 262, IrcCommand::RPL_ENDOFTRACE, 3, 3
263, myirc::IrcCommand::RPL_LOAD2HI, 3, 3 263, IrcCommand::RPL_LOAD2HI, 3, 3
265, myirc::IrcCommand::RPL_LOCALUSERS, 4, 4 265, IrcCommand::RPL_LOCALUSERS, 4, 4
266, myirc::IrcCommand::RPL_GLOBALUSERS, 4, 4 266, IrcCommand::RPL_GLOBALUSERS, 4, 4
270, myirc::IrcCommand::RPL_PRIVS, 3, 3 270, IrcCommand::RPL_PRIVS, 3, 3
276, myirc::IrcCommand::RPL_WHOISCERTFP, 3, 3 276, IrcCommand::RPL_WHOISCERTFP, 3, 3
281, myirc::IrcCommand::RPL_ACCEPTLIST, 1, 15 281, IrcCommand::RPL_ACCEPTLIST, 1, 15
282, myirc::IrcCommand::RPL_ENDOFACCEPT, 2, 2 282, IrcCommand::RPL_ENDOFACCEPT, 2, 2
300, myirc::IrcCommand::RPL_NONE 300, IrcCommand::RPL_NONE
301, myirc::IrcCommand::RPL_AWAY, 3, 3 301, IrcCommand::RPL_AWAY, 3, 3
302, myirc::IrcCommand::RPL_USERHOST, 2, 2 302, IrcCommand::RPL_USERHOST, 2, 2
303, myirc::IrcCommand::RPL_ISON, 2, 2 303, IrcCommand::RPL_ISON, 2, 2
304, myirc::IrcCommand::RPL_TEXT 304, IrcCommand::RPL_TEXT
305, myirc::IrcCommand::RPL_UNAWAY, 2, 2 305, IrcCommand::RPL_UNAWAY, 2, 2
306, myirc::IrcCommand::RPL_NOWAWAY, 2, 2 306, IrcCommand::RPL_NOWAWAY, 2, 2
310, myirc::IrcCommand::RPL_WHOISHELPOP, 3, 3 310, IrcCommand::RPL_WHOISHELPOP, 3, 3
311, myirc::IrcCommand::RPL_WHOISUSER, 6, 6 311, IrcCommand::RPL_WHOISUSER, 6, 6
312, myirc::IrcCommand::RPL_WHOISSERVER, 4, 4 312, IrcCommand::RPL_WHOISSERVER, 4, 4
313, myirc::IrcCommand::RPL_WHOISOPERATOR, 3, 3 313, IrcCommand::RPL_WHOISOPERATOR, 3, 3
314, myirc::IrcCommand::RPL_WHOWASUSER, 6, 6 314, IrcCommand::RPL_WHOWASUSER, 6, 6
369, myirc::IrcCommand::RPL_ENDOFWHOWAS, 3, 3 369, IrcCommand::RPL_ENDOFWHOWAS, 3, 3
316, myirc::IrcCommand::RPL_WHOISCHANOP 316, IrcCommand::RPL_WHOISCHANOP
317, myirc::IrcCommand::RPL_WHOISIDLE, 5, 5 317, IrcCommand::RPL_WHOISIDLE, 5, 5
318, myirc::IrcCommand::RPL_ENDOFWHOIS, 3, 3 318, IrcCommand::RPL_ENDOFWHOIS, 3, 3
319, myirc::IrcCommand::RPL_WHOISCHANNELS, 3, 3 319, IrcCommand::RPL_WHOISCHANNELS, 3, 3
320, myirc::IrcCommand::RPL_WHOISSPECIAL, 3, 3 320, IrcCommand::RPL_WHOISSPECIAL, 3, 3
321, myirc::IrcCommand::RPL_LISTSTART, 3, 3 321, IrcCommand::RPL_LISTSTART, 3, 3
322, myirc::IrcCommand::RPL_LIST, 4, 4 322, IrcCommand::RPL_LIST, 4, 4
323, myirc::IrcCommand::RPL_LISTEND, 2, 2 323, IrcCommand::RPL_LISTEND, 2, 2
324, myirc::IrcCommand::RPL_CHANNELMODEIS, 3, 3 324, IrcCommand::RPL_CHANNELMODEIS, 3, 3
325, myirc::IrcCommand::RPL_CHANNELMLOCK, 4, 4 325, IrcCommand::RPL_CHANNELMLOCK, 4, 4
328, myirc::IrcCommand::RPL_CHANNELURL 328, IrcCommand::RPL_CHANNELURL
329, myirc::IrcCommand::RPL_CREATIONTIME, 3, 3 329, IrcCommand::RPL_CREATIONTIME, 3, 3
330, myirc::IrcCommand::RPL_WHOISLOGGEDIN, 4, 4 330, IrcCommand::RPL_WHOISLOGGEDIN, 4, 4
331, myirc::IrcCommand::RPL_NOTOPIC, 3, 3 331, IrcCommand::RPL_NOTOPIC, 3, 3
332, myirc::IrcCommand::RPL_TOPIC, 3, 3 332, IrcCommand::RPL_TOPIC, 3, 3
333, myirc::IrcCommand::RPL_TOPICWHOTIME, 4, 4 333, IrcCommand::RPL_TOPICWHOTIME, 4, 4
338, myirc::IrcCommand::RPL_WHOISACTUALLY, 4, 4 338, IrcCommand::RPL_WHOISACTUALLY, 4, 4
341, myirc::IrcCommand::RPL_INVITING, 3, 3 341, IrcCommand::RPL_INVITING, 3, 3
342, myirc::IrcCommand::RPL_SUMMONING 342, IrcCommand::RPL_SUMMONING
346, myirc::IrcCommand::RPL_INVITELIST, 5, 5 346, IrcCommand::RPL_INVITELIST, 5, 5
347, myirc::IrcCommand::RPL_ENDOFINVITELIST, 3, 3 347, IrcCommand::RPL_ENDOFINVITELIST, 3, 3
348, myirc::IrcCommand::RPL_EXCEPTLIST, 5, 5 348, IrcCommand::RPL_EXCEPTLIST, 5, 5
349, myirc::IrcCommand::RPL_ENDOFEXCEPTLIST, 3, 3 349, IrcCommand::RPL_ENDOFEXCEPTLIST, 3, 3
351, myirc::IrcCommand::RPL_VERSION, 4, 4 351, IrcCommand::RPL_VERSION, 4, 4
352, myirc::IrcCommand::RPL_WHOREPLY, 8, 8 352, IrcCommand::RPL_WHOREPLY, 8, 8
354, myirc::IrcCommand::RPL_WHOSPCRPL 354, IrcCommand::RPL_WHOSPCRPL
315, myirc::IrcCommand::RPL_ENDOFWHO, 3, 3 315, IrcCommand::RPL_ENDOFWHO, 3, 3
353, myirc::IrcCommand::RPL_NAMREPLY, 4, 4 353, IrcCommand::RPL_NAMREPLY, 4, 4
360, myirc::IrcCommand::RPL_WHOWASREAL, 3, 3 360, IrcCommand::RPL_WHOWASREAL, 3, 3
366, myirc::IrcCommand::RPL_ENDOFNAMES, 3, 3 366, IrcCommand::RPL_ENDOFNAMES, 3, 3
361, myirc::IrcCommand::RPL_KILLDONE 361, IrcCommand::RPL_KILLDONE
362, myirc::IrcCommand::RPL_CLOSING, 3, 3 362, IrcCommand::RPL_CLOSING, 3, 3
363, myirc::IrcCommand::RPL_CLOSEEND, 3, 3 363, IrcCommand::RPL_CLOSEEND, 3, 3
364, myirc::IrcCommand::RPL_LINKS, 4, 4 364, IrcCommand::RPL_LINKS, 4, 4
365, myirc::IrcCommand::RPL_ENDOFLINKS, 3, 3 365, IrcCommand::RPL_ENDOFLINKS, 3, 3
367, myirc::IrcCommand::RPL_BANLIST, 5, 5 367, IrcCommand::RPL_BANLIST, 5, 5
368, myirc::IrcCommand::RPL_ENDOFBANLIST, 3, 3 368, IrcCommand::RPL_ENDOFBANLIST, 3, 3
371, myirc::IrcCommand::RPL_INFO, 2, 2 371, IrcCommand::RPL_INFO, 2, 2
372, myirc::IrcCommand::RPL_MOTD, 2, 2 372, IrcCommand::RPL_MOTD, 2, 2
373, myirc::IrcCommand::RPL_INFOSTART 373, IrcCommand::RPL_INFOSTART
374, myirc::IrcCommand::RPL_ENDOFINFO, 2, 2 374, IrcCommand::RPL_ENDOFINFO, 2, 2
375, myirc::IrcCommand::RPL_MOTDSTART, 2, 2 375, IrcCommand::RPL_MOTDSTART, 2, 2
376, myirc::IrcCommand::RPL_ENDOFMOTD, 2, 2 376, IrcCommand::RPL_ENDOFMOTD, 2, 2
378, myirc::IrcCommand::RPL_WHOISHOST, 3, 3 378, IrcCommand::RPL_WHOISHOST, 3, 3
381, myirc::IrcCommand::RPL_YOUREOPER, 2, 2 381, IrcCommand::RPL_YOUREOPER, 2, 2
382, myirc::IrcCommand::RPL_REHASHING, 3, 3 382, IrcCommand::RPL_REHASHING, 3, 3
384, myirc::IrcCommand::RPL_MYPORTIS 384, IrcCommand::RPL_MYPORTIS
385, myirc::IrcCommand::RPL_NOTOPERANYMORE 385, IrcCommand::RPL_NOTOPERANYMORE
386, myirc::IrcCommand::RPL_RSACHALLENGE, 2, 2 386, IrcCommand::RPL_RSACHALLENGE, 2, 2
391, myirc::IrcCommand::RPL_TIME, 3, 3 391, IrcCommand::RPL_TIME, 3, 3
392, myirc::IrcCommand::RPL_USERSSTART 392, IrcCommand::RPL_USERSSTART
393, myirc::IrcCommand::RPL_USERS 393, IrcCommand::RPL_USERS
394, myirc::IrcCommand::RPL_ENDOFUSERS 394, IrcCommand::RPL_ENDOFUSERS
395, myirc::IrcCommand::RPL_NOUSERS 395, IrcCommand::RPL_NOUSERS
396, myirc::IrcCommand::RPL_HOSTHIDDEN 396, IrcCommand::RPL_HOSTHIDDEN
401, myirc::IrcCommand::ERR_NOSUCHNICK, 3, 3 401, IrcCommand::ERR_NOSUCHNICK, 3, 3
402, myirc::IrcCommand::ERR_NOSUCHSERVER, 3, 3 402, IrcCommand::ERR_NOSUCHSERVER, 3, 3
403, myirc::IrcCommand::ERR_NOSUCHCHANNEL, 3, 3 403, IrcCommand::ERR_NOSUCHCHANNEL, 3, 3
404, myirc::IrcCommand::ERR_CANNOTSENDTOCHAN, 3, 3 404, IrcCommand::ERR_CANNOTSENDTOCHAN, 3, 3
405, myirc::IrcCommand::ERR_TOOMANYCHANNELS, 3, 3 405, IrcCommand::ERR_TOOMANYCHANNELS, 3, 3
406, myirc::IrcCommand::ERR_WASNOSUCHNICK, 3, 3 406, IrcCommand::ERR_WASNOSUCHNICK, 3, 3
407, myirc::IrcCommand::ERR_TOOMANYTARGETS, 3, 3 407, IrcCommand::ERR_TOOMANYTARGETS, 3, 3
409, myirc::IrcCommand::ERR_NOORIGIN, 2, 2 409, IrcCommand::ERR_NOORIGIN, 2, 2
410, myirc::IrcCommand::ERR_INVALIDCAPCMD, 3, 3 410, IrcCommand::ERR_INVALIDCAPCMD, 3, 3
411, myirc::IrcCommand::ERR_NORECIPIENT, 2, 2 411, IrcCommand::ERR_NORECIPIENT, 2, 2
412, myirc::IrcCommand::ERR_NOTEXTTOSEND, 2, 2 412, IrcCommand::ERR_NOTEXTTOSEND, 2, 2
413, myirc::IrcCommand::ERR_NOTOPLEVEL, 3, 3 413, IrcCommand::ERR_NOTOPLEVEL, 3, 3
414, myirc::IrcCommand::ERR_WILDTOPLEVEL, 3, 3 414, IrcCommand::ERR_WILDTOPLEVEL, 3, 3
415, myirc::IrcCommand::ERR_MSGNEEDREGGEDNICK, 3, 3 415, IrcCommand::ERR_MSGNEEDREGGEDNICK, 3, 3
416, myirc::IrcCommand::ERR_TOOMANYMATCHES, 3, 3 416, IrcCommand::ERR_TOOMANYMATCHES, 3, 3
421, myirc::IrcCommand::ERR_UNKNOWNCOMMAND, 3, 3 421, IrcCommand::ERR_UNKNOWNCOMMAND, 3, 3
422, myirc::IrcCommand::ERR_NOMOTD, 2, 2 422, IrcCommand::ERR_NOMOTD, 2, 2
423, myirc::IrcCommand::ERR_NOADMININFO 423, IrcCommand::ERR_NOADMININFO
424, myirc::IrcCommand::ERR_FILEERROR 424, IrcCommand::ERR_FILEERROR
431, myirc::IrcCommand::ERR_NONICKNAMEGIVEN, 2, 2 431, IrcCommand::ERR_NONICKNAMEGIVEN, 2, 2
432, myirc::IrcCommand::ERR_ERRONEUSNICKNAME, 3, 3 432, IrcCommand::ERR_ERRONEUSNICKNAME, 3, 3
433, myirc::IrcCommand::ERR_NICKNAMEINUSE, 3, 3 433, IrcCommand::ERR_NICKNAMEINUSE, 3, 3
435, myirc::IrcCommand::ERR_BANNICKCHANGE, 3, 3 435, IrcCommand::ERR_BANNICKCHANGE, 3, 3
436, myirc::IrcCommand::ERR_NICKCOLLISION, 3, 3 436, IrcCommand::ERR_NICKCOLLISION, 3, 3
437, myirc::IrcCommand::ERR_UNAVAILRESOURCE, 3, 3 437, IrcCommand::ERR_UNAVAILRESOURCE, 3, 3
438, myirc::IrcCommand::ERR_NICKTOOFAST, 4, 4 438, IrcCommand::ERR_NICKTOOFAST, 4, 4
440, myirc::IrcCommand::ERR_SERVICESDOWN, 3, 3 440, IrcCommand::ERR_SERVICESDOWN, 3, 3
441, myirc::IrcCommand::ERR_USERNOTINCHANNEL, 4, 4 441, IrcCommand::ERR_USERNOTINCHANNEL, 4, 4
442, myirc::IrcCommand::ERR_NOTONCHANNEL, 3, 3 442, IrcCommand::ERR_NOTONCHANNEL, 3, 3
443, myirc::IrcCommand::ERR_USERONCHANNEL, 4, 4 443, IrcCommand::ERR_USERONCHANNEL, 4, 4
444, myirc::IrcCommand::ERR_NOLOGIN 444, IrcCommand::ERR_NOLOGIN
445, myirc::IrcCommand::ERR_SUMMONDISABLED 445, IrcCommand::ERR_SUMMONDISABLED
446, myirc::IrcCommand::ERR_USERSDISABLED 446, IrcCommand::ERR_USERSDISABLED
451, myirc::IrcCommand::ERR_NOTREGISTERED, 2, 2 451, IrcCommand::ERR_NOTREGISTERED, 2, 2
456, myirc::IrcCommand::ERR_ACCEPTFULL, 2, 2 456, IrcCommand::ERR_ACCEPTFULL, 2, 2
457, myirc::IrcCommand::ERR_ACCEPTEXIST, 3, 3 457, IrcCommand::ERR_ACCEPTEXIST, 3, 3
458, myirc::IrcCommand::ERR_ACCEPTNOT, 3, 3 458, IrcCommand::ERR_ACCEPTNOT, 3, 3
461, myirc::IrcCommand::ERR_NEEDMOREPARAMS, 3, 3 461, IrcCommand::ERR_NEEDMOREPARAMS, 3, 3
462, myirc::IrcCommand::ERR_ALREADYREGISTRED, 2, 2 462, IrcCommand::ERR_ALREADYREGISTRED, 2, 2
463, myirc::IrcCommand::ERR_NOPERMFORHOST 463, IrcCommand::ERR_NOPERMFORHOST
464, myirc::IrcCommand::ERR_PASSWDMISMATCH, 2, 2 464, IrcCommand::ERR_PASSWDMISMATCH, 2, 2
465, myirc::IrcCommand::ERR_YOUREBANNEDCREEP, 2, 2 465, IrcCommand::ERR_YOUREBANNEDCREEP, 2, 2
466, myirc::IrcCommand::ERR_YOUWILLBEBANNED 466, IrcCommand::ERR_YOUWILLBEBANNED
467, myirc::IrcCommand::ERR_KEYSET 467, IrcCommand::ERR_KEYSET
470, myirc::IrcCommand::ERR_LINKCHANNEL, 4, 4 470, IrcCommand::ERR_LINKCHANNEL, 4, 4
471, myirc::IrcCommand::ERR_CHANNELISFULL, 3, 3 471, IrcCommand::ERR_CHANNELISFULL, 3, 3
472, myirc::IrcCommand::ERR_UNKNOWNMODE, 3, 3 472, IrcCommand::ERR_UNKNOWNMODE, 3, 3
473, myirc::IrcCommand::ERR_INVITEONLYCHAN, 3, 3 473, IrcCommand::ERR_INVITEONLYCHAN, 3, 3
474, myirc::IrcCommand::ERR_BANNEDFROMCHAN, 3, 3 474, IrcCommand::ERR_BANNEDFROMCHAN, 3, 3
475, myirc::IrcCommand::ERR_BADCHANNELKEY, 3, 3 475, IrcCommand::ERR_BADCHANNELKEY, 3, 3
476, myirc::IrcCommand::ERR_BADCHANMASK 476, IrcCommand::ERR_BADCHANMASK
477, myirc::IrcCommand::ERR_NEEDREGGEDNICK, 3, 3 477, IrcCommand::ERR_NEEDREGGEDNICK, 3, 3
478, myirc::IrcCommand::ERR_BANLISTFULL, 4, 4 478, IrcCommand::ERR_BANLISTFULL, 4, 4
479, myirc::IrcCommand::ERR_BADCHANNAME, 3, 3 479, IrcCommand::ERR_BADCHANNAME, 3, 3
480, myirc::IrcCommand::ERR_THROTTLE, 3, 3 480, IrcCommand::ERR_THROTTLE, 3, 3
481, myirc::IrcCommand::ERR_NOPRIVILEGES, 2, 2 481, IrcCommand::ERR_NOPRIVILEGES, 2, 2
482, myirc::IrcCommand::ERR_CHANOPRIVSNEEDED, 3, 3 482, IrcCommand::ERR_CHANOPRIVSNEEDED, 3, 3
483, myirc::IrcCommand::ERR_CANTKILLSERVER, 2, 2 483, IrcCommand::ERR_CANTKILLSERVER, 2, 2
484, myirc::IrcCommand::ERR_ISCHANSERVICE, 4, 4 484, IrcCommand::ERR_ISCHANSERVICE, 4, 4
485, myirc::IrcCommand::ERR_BANNEDNICK 485, IrcCommand::ERR_BANNEDNICK
486, myirc::IrcCommand::ERR_NONONREG, 3, 3 486, IrcCommand::ERR_NONONREG, 3, 3
489, myirc::IrcCommand::ERR_VOICENEEDED, 3, 3 489, IrcCommand::ERR_VOICENEEDED, 3, 3
491, myirc::IrcCommand::ERR_NOOPERHOST, 2, 2 491, IrcCommand::ERR_NOOPERHOST, 2, 2
492, myirc::IrcCommand::ERR_CANNOTSENDTOUSER, 2, 2 492, IrcCommand::ERR_CANNOTSENDTOUSER, 2, 2
494, myirc::IrcCommand::ERR_OWNMODE, 3, 3 494, IrcCommand::ERR_OWNMODE, 3, 3
501, myirc::IrcCommand::ERR_UMODEUNKNOWNFLAG, 2, 2 501, IrcCommand::ERR_UMODEUNKNOWNFLAG, 2, 2
502, myirc::IrcCommand::ERR_USERSDONTMATCH, 2, 2 502, IrcCommand::ERR_USERSDONTMATCH, 2, 2
503, myirc::IrcCommand::ERR_GHOSTEDCLIENT 503, IrcCommand::ERR_GHOSTEDCLIENT
504, myirc::IrcCommand::ERR_USERNOTONSERV, 3, 3 504, IrcCommand::ERR_USERNOTONSERV, 3, 3
513, myirc::IrcCommand::ERR_WRONGPONG, 2, 2 513, IrcCommand::ERR_WRONGPONG, 2, 2
517, myirc::IrcCommand::ERR_DISABLED, 3, 3 517, IrcCommand::ERR_DISABLED, 3, 3
524, myirc::IrcCommand::ERR_HELPNOTFOUND, 3, 3 524, IrcCommand::ERR_HELPNOTFOUND, 3, 3
670, myirc::IrcCommand::RPL_STARTTLS, 2, 2 670, IrcCommand::RPL_STARTTLS, 2, 2
671, myirc::IrcCommand::RPL_WHOISSECURE, 3, 3 671, IrcCommand::RPL_WHOISSECURE, 3, 3
691, myirc::IrcCommand::ERR_STARTTLS, 2, 2 691, IrcCommand::ERR_STARTTLS, 2, 2
702, myirc::IrcCommand::RPL_MODLIST, 5, 5 702, IrcCommand::RPL_MODLIST, 5, 5
703, myirc::IrcCommand::RPL_ENDOFMODLIST, 2, 2 703, IrcCommand::RPL_ENDOFMODLIST, 2, 2
704, myirc::IrcCommand::RPL_HELPSTART, 3, 3 704, IrcCommand::RPL_HELPSTART, 3, 3
705, myirc::IrcCommand::RPL_HELPTXT, 3, 3 705, IrcCommand::RPL_HELPTXT, 3, 3
706, myirc::IrcCommand::RPL_ENDOFHELP, 3, 3 706, IrcCommand::RPL_ENDOFHELP, 3, 3
707, myirc::IrcCommand::ERR_TARGCHANGE, 3, 3 707, IrcCommand::ERR_TARGCHANGE, 3, 3
708, myirc::IrcCommand::RPL_ETRACEFULL, 10, 10 708, IrcCommand::RPL_ETRACEFULL, 10, 10
709, myirc::IrcCommand::RPL_ETRACE, 8, 8 709, IrcCommand::RPL_ETRACE, 8, 8
710, myirc::IrcCommand::RPL_KNOCK, 4, 4 710, IrcCommand::RPL_KNOCK, 4, 4
711, myirc::IrcCommand::RPL_KNOCKDLVR, 3, 3 711, IrcCommand::RPL_KNOCKDLVR, 3, 3
712, myirc::IrcCommand::ERR_TOOMANYKNOCK, 3, 3 712, IrcCommand::ERR_TOOMANYKNOCK, 3, 3
713, myirc::IrcCommand::ERR_CHANOPEN, 3, 3 713, IrcCommand::ERR_CHANOPEN, 3, 3
714, myirc::IrcCommand::ERR_KNOCKONCHAN, 3, 3 714, IrcCommand::ERR_KNOCKONCHAN, 3, 3
715, myirc::IrcCommand::ERR_KNOCKDISABLED, 2, 2 715, IrcCommand::ERR_KNOCKDISABLED, 2, 2
716, myirc::IrcCommand::ERR_TARGUMODEG, 3, 3 716, IrcCommand::ERR_TARGUMODEG, 3, 3
717, myirc::IrcCommand::RPL_TARGNOTIFY 717, IrcCommand::RPL_TARGNOTIFY
718, myirc::IrcCommand::RPL_UMODEGMSG, 4, 4 718, IrcCommand::RPL_UMODEGMSG, 4, 4
720, myirc::IrcCommand::RPL_OMOTDSTART, 2, 2 720, IrcCommand::RPL_OMOTDSTART, 2, 2
721, myirc::IrcCommand::RPL_OMOTD, 2, 2 721, IrcCommand::RPL_OMOTD, 2, 2
722, myirc::IrcCommand::RPL_ENDOFOMOTD, 2, 2 722, IrcCommand::RPL_ENDOFOMOTD, 2, 2
723, myirc::IrcCommand::ERR_NOPRIVS, 3, 3 723, IrcCommand::ERR_NOPRIVS, 3, 3
724, myirc::IrcCommand::RPL_TESTMASK 724, IrcCommand::RPL_TESTMASK
725, myirc::IrcCommand::RPL_TESTLINE, 5, 5 725, IrcCommand::RPL_TESTLINE, 5, 5
726, myirc::IrcCommand::RPL_NOTESTLINE 726, IrcCommand::RPL_NOTESTLINE
727, myirc::IrcCommand::RPL_TESTMASKGECO, 6, 6 727, IrcCommand::RPL_TESTMASKGECO, 6, 6
728, myirc::IrcCommand::RPL_QUIETLIST, 6, 6 728, IrcCommand::RPL_QUIETLIST, 6, 6
729, myirc::IrcCommand::RPL_ENDOFQUIETLIS, 4, 4 729, IrcCommand::RPL_ENDOFQUIETLIS, 4, 4
730, myirc::IrcCommand::RPL_MONONLINE, 2, 2 730, IrcCommand::RPL_MONONLINE, 2, 2
731, myirc::IrcCommand::RPL_MONOFFLINE, 2, 2 731, IrcCommand::RPL_MONOFFLINE, 2, 2
732, myirc::IrcCommand::RPL_MONLIST, 2, 2 732, IrcCommand::RPL_MONLIST, 2, 2
733, myirc::IrcCommand::RPL_ENDOFMONLIS, 2, 2 733, IrcCommand::RPL_ENDOFMONLIS, 2, 2
734, myirc::IrcCommand::ERR_MONLISTFULL, 4, 4 734, IrcCommand::ERR_MONLISTFULL, 4, 4
740, myirc::IrcCommand::RPL_RSACHALLENGE2, 2, 2 740, IrcCommand::RPL_RSACHALLENGE2, 2, 2
741, myirc::IrcCommand::RPL_ENDOFRSACHALLENGE2, 2, 2 741, IrcCommand::RPL_ENDOFRSACHALLENGE2, 2, 2
742, myirc::IrcCommand::ERR_MLOCKRESTRICTE, 5, 5 742, IrcCommand::ERR_MLOCKRESTRICTE, 5, 5
743, myirc::IrcCommand::ERR_INVALIDBAN, 5, 5 743, IrcCommand::ERR_INVALIDBAN, 5, 5
744, myirc::IrcCommand::ERR_TOPICLOCK 744, IrcCommand::ERR_TOPICLOCK
750, myirc::IrcCommand::RPL_SCANMATCHED, 3, 3 750, IrcCommand::RPL_SCANMATCHED, 3, 3
751, myirc::IrcCommand::RPL_SCANUMODES, 8, 8 751, IrcCommand::RPL_SCANUMODES, 8, 8
900, myirc::IrcCommand::RPL_LOGGEDIN, 4, 4 900, IrcCommand::RPL_LOGGEDIN, 4, 4
901, myirc::IrcCommand::RPL_LOGGEDOUT, 3, 3 901, IrcCommand::RPL_LOGGEDOUT, 3, 3
902, myirc::IrcCommand::ERR_NICKLOCKED, 2, 2 902, IrcCommand::ERR_NICKLOCKED, 2, 2
903, myirc::IrcCommand::RPL_SASLSUCCESS, 2, 2 903, IrcCommand::RPL_SASLSUCCESS, 2, 2
904, myirc::IrcCommand::ERR_SASLFAIL, 2, 2 904, IrcCommand::ERR_SASLFAIL, 2, 2
905, myirc::IrcCommand::ERR_SASLTOOLONG, 2, 2 905, IrcCommand::ERR_SASLTOOLONG, 2, 2
906, myirc::IrcCommand::ERR_SASLABORTED, 2, 2 906, IrcCommand::ERR_SASLABORTED, 2, 2
907, myirc::IrcCommand::ERR_SASLALREADY, 2, 2 907, IrcCommand::ERR_SASLALREADY, 2, 2
908, myirc::IrcCommand::RPL_SASLMECHS, 3, 3 908, IrcCommand::RPL_SASLMECHS, 3, 3
ACCOUNT, myirc::IrcCommand::ACCOUNT, 1, 1 ACCOUNT, IrcCommand::ACCOUNT, 1, 1
AUTHENTICATE, myirc::IrcCommand::AUTHENTICATE, 1, 1 AUTHENTICATE, IrcCommand::AUTHENTICATE, 1, 1
AWAY, myirc::IrcCommand::AWAY, 0, 1 AWAY, IrcCommand::AWAY, 0, 1
BATCH, myirc::IrcCommand::BATCH BATCH, IrcCommand::BATCH
BOUNCER, myirc::IrcCommand::BOUNCER BOUNCER, IrcCommand::BOUNCER
CAP, myirc::IrcCommand::CAP, 1, 15 CAP, IrcCommand::CAP, 1, 15
CHGHOST, myirc::IrcCommand::CHGHOST, 2, 2 CHGHOST, IrcCommand::CHGHOST, 2, 2
ERROR, myirc::IrcCommand::ERROR, 1, 1 ERROR, IrcCommand::ERROR, 1, 1
INVITE, myirc::IrcCommand::INVITE, 2, 2 INVITE, IrcCommand::INVITE, 2, 2
JOIN, myirc::IrcCommand::JOIN, 1, 3 JOIN, IrcCommand::JOIN, 1, 3
KICK, myirc::IrcCommand::KICK, 3, 3 KICK, IrcCommand::KICK, 3, 3
KILL, myirc::IrcCommand::KILL, 2, 2 KILL, IrcCommand::KILL, 2, 2
MODE, myirc::IrcCommand::MODE, 2, 15 MODE, IrcCommand::MODE, 2, 15
NICK, myirc::IrcCommand::NICK, 1, 1 NICK, IrcCommand::NICK, 1, 1
NOTICE, myirc::IrcCommand::NOTICE, 2, 2 NOTICE, IrcCommand::NOTICE, 2, 2
PART, myirc::IrcCommand::PART, 1, 2 PART, IrcCommand::PART, 1, 2
PING, myirc::IrcCommand::PING, 1, 1 PING, IrcCommand::PING, 1, 1
PONG, myirc::IrcCommand::PONG, 1, 2 PONG, IrcCommand::PONG, 1, 2
PRIVMSG, myirc::IrcCommand::PRIVMSG, 2, 2 PRIVMSG, IrcCommand::PRIVMSG, 2, 2
QUIT, myirc::IrcCommand::QUIT, 1, 1 QUIT, IrcCommand::QUIT, 1, 1
SETNAME, myirc::IrcCommand::SETNAME, 1, 1 SETNAME, IrcCommand::SETNAME, 1, 1
TAGMSG, myirc::IrcCommand::TAGMSG, 1, 1 TAGMSG, IrcCommand::TAGMSG, 1, 1
TOPIC, myirc::IrcCommand::TOPIC, 2, 2 TOPIC, IrcCommand::TOPIC, 2, 2
WALLOPS, myirc::IrcCommand::WALLOPS, 1, 1 WALLOPS, IrcCommand::WALLOPS, 1, 1