diff --git a/CMakeLists.txt b/CMakeLists.txt index fc299c8..5dc3c51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,6 @@ project(xbot ) find_package(Boost REQUIRED) - include(FetchContent) FetchContent_Declare( @@ -24,5 +23,16 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(eventpp) -add_executable(xbot main.cpp ircmsg.cpp settings.cpp connection.cpp thread.cpp snote_thread.cpp watchdog_thread.cpp write_irc.cpp ping_thread.cpp irc_parse_thread.cpp registration_thread.cpp) +add_custom_command( + OUTPUT irc_commands.inc + COMMAND + gperf + -C -Z IrcCommandHash -K text -L C++ -t + --output-file irc_commands.inc + ${CMAKE_CURRENT_SOURCE_DIR}/irc_commands.gperf + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/irc_commands.gperf + VERBATIM) + +add_executable(xbot main.cpp irc_commands.inc ircmsg.cpp settings.cpp connection.cpp thread.cpp snote_thread.cpp watchdog_thread.cpp write_irc.cpp ping_thread.cpp irc_parse_thread.cpp registration_thread.cpp) +target_include_directories(xbot PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(xbot PRIVATE Boost::headers tomlplusplus_tomlplusplus eventpp) diff --git a/irc_parse_thread.cpp b/irc_parse_thread.cpp index 8af0370..d8f0f31 100644 --- a/irc_parse_thread.cpp +++ b/irc_parse_thread.cpp @@ -2,10 +2,25 @@ #include "connection.hpp" +#include + +namespace { + +#include "irc_commands.inc" + +} // namespace + auto irc_parse_thread(Connection * connection) -> void { connection->add_listener([connection](LineEvent const& event) { - connection->make_event(parse_irc_message(event.line)); + auto const msg = parse_irc_message(event.line); + auto const recognized = IrcCommandHash::in_word_set(msg.command.data(), msg.command.size()); + auto const command + = recognized + && recognized->min_args <= msg.args.size() + && recognized->max_args >= msg.args.size() + ? recognized->command : IrcCommand::UNKNOWN; + connection->make_event(command, msg); }); } \ No newline at end of file diff --git a/irc_parse_thread.hpp b/irc_parse_thread.hpp index b602d2d..a6176e1 100644 --- a/irc_parse_thread.hpp +++ b/irc_parse_thread.hpp @@ -2,12 +2,295 @@ #include "thread.hpp" +#include + class Connection; + +enum class IrcCommand +{ + UNKNOWN, + RPL_WELCOME, + RPL_YOURHOST, + RPL_CREATED, + RPL_MYINFO, + RPL_ISUPPORT, + RPL_SNOMASK, + RPL_REDIR, + RPL_MAP, + RPL_MAPMORE, + RPL_MAPEND, + RPL_SAVENICK, + RPL_TRACELINK, + RPL_TRACECONNECTING, + RPL_TRACEHANDSHAKE, + RPL_TRACEUNKNOWN, + RPL_TRACEOPERATOR, + RPL_TRACEUSER, + RPL_TRACESERVER, + RPL_TRACENEWTYPE, + RPL_TRACECLASS, + RPL_STATSLINKINFO, + RPL_STATSCOMMANDS, + RPL_STATSCLINE, + RPL_STATSNLINE, + RPL_STATSILINE, + RPL_STATSKLINE, + RPL_STATSQLINE, + RPL_STATSYLINE, + RPL_ENDOFSTATS, + RPL_STATSPLINE, + RPL_UMODEIS, + RPL_STATSFLINE, + RPL_STATSDLINE, + RPL_SERVLIST, + RPL_SERVLISTEND, + RPL_STATSLLINE, + RPL_STATSUPTIME, + RPL_STATSOLINE, + RPL_STATSHLINE, + RPL_STATSSLINE, + RPL_STATSXLINE, + RPL_STATSULINE, + RPL_STATSDEBUG, + RPL_STATSCONN, + RPL_LUSERCLIENT, + RPL_LUSEROP, + RPL_LUSERUNKNOWN, + RPL_LUSERCHANNELS, + RPL_LUSERME, + RPL_ADMINME, + RPL_ADMINLOC1, + RPL_ADMINLOC2, + RPL_ADMINEMAIL, + RPL_TRACELOG, + RPL_ENDOFTRACE, + RPL_LOAD2HI, + RPL_LOCALUSERS, + RPL_GLOBALUSERS, + RPL_PRIVS, + RPL_WHOISCERTFP, + RPL_ACCEPTLIST, + RPL_ENDOFACCEPT, + RPL_NONE, + RPL_AWAY, + RPL_USERHOST, + RPL_ISON, + RPL_TEXT, + RPL_UNAWAY, + RPL_NOWAWAY, + RPL_WHOISHELPOP, + RPL_WHOISUSER, + RPL_WHOISSERVER, + RPL_WHOISOPERATOR, + RPL_WHOWASUSER, + RPL_ENDOFWHOWAS, + RPL_WHOISCHANOP, + RPL_WHOISIDLE, + RPL_ENDOFWHOIS, + RPL_WHOISCHANNELS, + RPL_WHOISSPECIAL, + RPL_LISTSTART, + RPL_LIST, + RPL_LISTEND, + RPL_CHANNELMODEIS, + RPL_CHANNELMLOCK, + RPL_CHANNELURL, + RPL_CREATIONTIME, + RPL_WHOISLOGGEDIN, + RPL_NOTOPIC, + RPL_TOPIC, + RPL_TOPICWHOTIME, + RPL_WHOISTEXT, + RPL_WHOISACTUALLY, + RPL_INVITING, + RPL_SUMMONING, + RPL_INVITELIST, + RPL_ENDOFINVITELIST, + RPL_EXCEPTLIST, + RPL_ENDOFEXCEPTLIST, + RPL_VERSION, + RPL_WHOREPLY, + RPL_WHOSPCRPL, + RPL_ENDOFWHO, + RPL_NAMREPLY, + RPL_WHOWASREAL, + RPL_ENDOFNAMES, + RPL_KILLDONE, + RPL_CLOSING, + RPL_CLOSEEND, + RPL_LINKS, + RPL_ENDOFLINKS, + RPL_BANLIST, + RPL_ENDOFBANLIST, + RPL_INFO, + RPL_MOTD, + RPL_INFOSTART, + RPL_ENDOFINFO, + RPL_MOTDSTART, + RPL_ENDOFMOTD, + RPL_WHOISHOST, + RPL_YOUREOPER, + RPL_REHASHING, + RPL_MYPORTIS, + RPL_NOTOPERANYMORE, + RPL_RSACHALLENGE, + RPL_TIME, + RPL_USERSSTART, + RPL_USERS, + RPL_ENDOFUSERS, + RPL_NOUSERS, + RPL_HOSTHIDDEN, + ERR_NOSUCHNICK, + ERR_NOSUCHSERVER, + ERR_NOSUCHCHANNEL, + ERR_CANNOTSENDTOCHAN, + ERR_TOOMANYCHANNELS, + ERR_WASNOSUCHNICK, + ERR_TOOMANYTARGETS, + ERR_NOORIGIN, + ERR_INVALIDCAPCMD, + ERR_NORECIPIENT, + ERR_NOTEXTTOSEND, + ERR_NOTOPLEVEL, + ERR_WILDTOPLEVEL, + ERR_MSGNEEDREGGEDNICK, + ERR_TOOMANYMATCHES, + ERR_UNKNOWNCOMMAND, + ERR_NOMOTD, + ERR_NOADMININFO, + ERR_FILEERROR, + ERR_NONICKNAMEGIVEN, + ERR_ERRONEUSNICKNAME, + ERR_NICKNAMEINUSE, + ERR_BANNICKCHANGE, + ERR_NICKCOLLISION, + ERR_UNAVAILRESOURCE, + ERR_NICKTOOFAST, + ERR_SERVICESDOWN, + ERR_USERNOTINCHANNEL, + ERR_NOTONCHANNEL, + ERR_USERONCHANNEL, + ERR_NOLOGIN, + ERR_SUMMONDISABLED, + ERR_USERSDISABLED, + ERR_NOTREGISTERED, + ERR_ACCEPTFULL, + ERR_ACCEPTEXIST, + ERR_ACCEPTNOT, + ERR_NEEDMOREPARAMS, + ERR_ALREADYREGISTRED, + ERR_NOPERMFORHOST, + ERR_PASSWDMISMATCH, + ERR_YOUREBANNEDCREEP, + ERR_YOUWILLBEBANNED, + ERR_KEYSET, + ERR_LINKCHANNEL, + ERR_CHANNELISFULL, + ERR_UNKNOWNMODE, + ERR_INVITEONLYCHAN, + ERR_BANNEDFROMCHAN, + ERR_BADCHANNELKEY, + ERR_BADCHANMASK, + ERR_NEEDREGGEDNICK, + ERR_BANLISTFULL, + ERR_BADCHANNAME, + ERR_THROTTLE, + ERR_NOPRIVILEGES, + ERR_CHANOPRIVSNEEDED, + ERR_CANTKILLSERVER, + ERR_ISCHANSERVICE, + ERR_BANNEDNICK, + ERR_NONONREG, + ERR_VOICENEEDED, + ERR_NOOPERHOST, + ERR_CANNOTSENDTOUSER, + ERR_OWNMODE, + ERR_UMODEUNKNOWNFLAG, + ERR_USERSDONTMATCH, + ERR_GHOSTEDCLIENT, + ERR_USERNOTONSERV, + ERR_WRONGPONG, + ERR_DISABLED, + ERR_HELPNOTFOUND, + RPL_STARTTLS, + RPL_WHOISSECURE, + ERR_STARTTLS, + RPL_MODLIST, + RPL_ENDOFMODLIST, + RPL_HELPSTART, + RPL_HELPTXT, + RPL_ENDOFHELP, + ERR_TARGCHANGE, + RPL_ETRACEFULL, + RPL_ETRACE, + RPL_KNOCK, + RPL_KNOCKDLVR, + ERR_TOOMANYKNOCK, + ERR_CHANOPEN, + ERR_KNOCKONCHAN, + ERR_KNOCKDISABLED, + ERR_TARGUMODEG, + RPL_TARGNOTIFY, + RPL_UMODEGMSG, + RPL_OMOTDSTART, + RPL_OMOTD, + RPL_ENDOFOMOTD, + ERR_NOPRIVS, + RPL_TESTMASK, + RPL_TESTLINE, + RPL_NOTESTLINE, + RPL_TESTMASKGECO, + RPL_QUIETLIST, + RPL_ENDOFQUIETLIS, + RPL_MONONLINE, + RPL_MONOFFLINE, + RPL_MONLIST, + RPL_ENDOFMONLIS, + ERR_MONLISTFULL, + RPL_RSACHALLENGE2, + RPL_ENDOFRSACHALLENGE2, + ERR_MLOCKRESTRICTE, + ERR_INVALIDBAN, + ERR_TOPICLOCK, + RPL_SCANMATCHED, + RPL_SCANUMODES, + RPL_LOGGEDIN, + RPL_LOGGEDOUT, + ERR_NICKLOCKED, + RPL_SASLSUCCESS, + ERR_SASLFAIL, + ERR_SASLTOOLONG, + ERR_SASLABORTED, + ERR_SASLALREADY, + RPL_SASLMECHS, + ACCOUNT, + AUTHENTICATE, + AWAY, + BATCH, + BOUNCER, + CAP, + CHGHOST, + ERROR, + JOIN, + KICK, + MODE, + NICK, + NOTICE, + PART, + PING, + PRIVMSG, + QUIT, + SETNAME, + TOPIC, +}; + struct IrcMsgEvent : Event { - IrcMsgEvent(IrcMsg irc) : irc{irc} {} - IrcMsg irc; + IrcMsgEvent(IrcCommand command, IrcMsg const& irc) + : command{command}, irc{irc} {} + IrcCommand command; + IrcMsg const& irc; }; auto irc_parse_thread(Connection * connection) -> void; diff --git a/main.cpp b/main.cpp index 0a898e3..6ffff18 100644 --- a/main.cpp +++ b/main.cpp @@ -36,10 +36,10 @@ auto unhandled_message_thread(Connection * connection) -> void { connection->add_listener([](IrcMsgEvent const& event) { - if (not event.handled_) + if (IrcCommand::UNKNOWN == event.command) { auto& irc = event.irc; - std::cout << "Unhandled message " << irc.command; + std::cout << "Unknown message " << irc.command; for (auto const arg : irc.args) { std::cout << " " << arg; diff --git a/ping_thread.cpp b/ping_thread.cpp index 6c7f949..b0ab2b6 100644 --- a/ping_thread.cpp +++ b/ping_thread.cpp @@ -8,7 +8,7 @@ auto ping_thread(Connection * connection) -> void connection->add_listener([connection](IrcMsgEvent& event) { auto& irc = event.irc; - if ("PING" == irc.command && 1 == irc.args.size()) + if (IrcCommand::PING == event.command) { write_irc(*connection, "PONG", irc.args[0]); event.handled_ = true; diff --git a/registration_thread.cpp b/registration_thread.cpp index 88ce87f..f5a8e02 100644 --- a/registration_thread.cpp +++ b/registration_thread.cpp @@ -116,7 +116,7 @@ auto RegistrationThread::send_req() -> void auto RegistrationThread::capack(IrcMsg const& msg) -> void { auto const n = msg.args.size(); - if ("CAP" == msg.command && n >= 2 && "*" == msg.args[0] && "ACK" == msg.args[1]) + if (n >= 2 && "*" == msg.args[0] && "ACK" == msg.args[1]) { auto in = std::istringstream{std::string{msg.args[2]}}; std::for_each( @@ -137,7 +137,7 @@ auto RegistrationThread::capack(IrcMsg const& msg) -> void auto RegistrationThread::capls(IrcMsg const& msg) -> void { auto const n = msg.args.size(); - if ("CAP" == msg.command && n >= 2 && "*" == msg.args[0] && "LS" == msg.args[1]) + if (n >= 2 && "*" == msg.args[0] && "LS" == msg.args[1]) { std::string_view const* kvs; bool last; @@ -201,10 +201,13 @@ auto registration_thread( std::string nickname ) -> void { - auto thread = std::make_shared(connection, password, username, realname, nickname); + auto const thread = std::make_shared(connection, password, username, realname, nickname); thread->message_handle_ = connection->add_listener([thread](IrcMsgEvent const& event) { - thread->on_msg(event.irc); + if (IrcCommand::CAP == event.command) + { + thread->on_msg(event.irc); + } }); thread->connect_handle_ = connection->add_listener([thread](ConnectEvent const&) {