use gperf

This commit is contained in:
Eric Mertens 2023-11-26 15:08:55 -08:00
parent 959d51f5f4
commit 4c2f783d74
6 changed files with 323 additions and 12 deletions

View File

@ -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)

View File

@ -2,10 +2,25 @@
#include "connection.hpp"
#include <cstring>
namespace {
#include "irc_commands.inc"
} // namespace
auto irc_parse_thread(Connection * connection) -> void
{
connection->add_listener<LineEvent>([connection](LineEvent const& event)
{
connection->make_event<IrcMsgEvent>(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<IrcMsgEvent>(command, msg);
});
}

View File

@ -2,12 +2,295 @@
#include "thread.hpp"
#include <string_view>
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;

View File

@ -36,10 +36,10 @@ auto unhandled_message_thread(Connection * connection) -> void
{
connection->add_listener<IrcMsgEvent>([](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;

View File

@ -8,7 +8,7 @@ auto ping_thread(Connection * connection) -> void
connection->add_listener<IrcMsgEvent>([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;

View File

@ -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<RegistrationThread>(connection, password, username, realname, nickname);
auto const thread = std::make_shared<RegistrationThread>(connection, password, username, realname, nickname);
thread->message_handle_ = connection->add_listener<IrcMsgEvent>([thread](IrcMsgEvent const& event)
{
if (IrcCommand::CAP == event.command)
{
thread->on_msg(event.irc);
}
});
thread->connect_handle_ = connection->add_listener<ConnectEvent>([thread](ConnectEvent const&)
{