diff --git a/CMakeLists.txt b/CMakeLists.txt index 5dc3c51..75b2506 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,10 @@ add_custom_command( 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) +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 + self_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_commands.gperf b/irc_commands.gperf index dfb4ba7..3dc7102 100644 --- a/irc_commands.gperf +++ b/irc_commands.gperf @@ -87,10 +87,10 @@ struct RecognizedCommand { 321, IrcCommand::RPL_LISTSTART, 3, 3 322, IrcCommand::RPL_LIST, 4, 4 323, IrcCommand::RPL_LISTEND, 2, 2 -324, IrcCommand::RPL_CHANNELMODEIS +324, IrcCommand::RPL_CHANNELMODEIS, 3, 3 325, IrcCommand::RPL_CHANNELMLOCK 328, IrcCommand::RPL_CHANNELURL -329, IrcCommand::RPL_CREATIONTIME +329, IrcCommand::RPL_CREATIONTIME, 3, 3 330, IrcCommand::RPL_WHOISLOGGEDIN, 4, 4 331, IrcCommand::RPL_NOTOPIC, 3, 3 332, IrcCommand::RPL_TOPIC, 3, 3 @@ -123,7 +123,7 @@ struct RecognizedCommand { 375, IrcCommand::RPL_MOTDSTART, 2, 2 376, IrcCommand::RPL_ENDOFMOTD, 2, 2 378, IrcCommand::RPL_WHOISHOST -381, IrcCommand::RPL_YOUREOPER +381, IrcCommand::RPL_YOUREOPER, 2, 2 382, IrcCommand::RPL_REHASHING 384, IrcCommand::RPL_MYPORTIS 385, IrcCommand::RPL_NOTOPERANYMORE @@ -155,7 +155,7 @@ struct RecognizedCommand { 424, IrcCommand::ERR_FILEERROR 431, IrcCommand::ERR_NONICKNAMEGIVEN 432, IrcCommand::ERR_ERRONEUSNICKNAME -433, IrcCommand::ERR_NICKNAMEINUSE +433, IrcCommand::ERR_NICKNAMEINUSE, 3, 3 435, IrcCommand::ERR_BANNICKCHANGE 436, IrcCommand::ERR_NICKCOLLISION 437, IrcCommand::ERR_UNAVAILRESOURCE @@ -200,7 +200,7 @@ struct RecognizedCommand { 492, IrcCommand::ERR_CANNOTSENDTOUSER 494, IrcCommand::ERR_OWNMODE 501, IrcCommand::ERR_UMODEUNKNOWNFLAG -502, IrcCommand::ERR_USERSDONTMATCH +502, IrcCommand::ERR_USERSDONTMATCH, 2, 2 503, IrcCommand::ERR_GHOSTEDCLIENT 504, IrcCommand::ERR_USERNOTONSERV 513, IrcCommand::ERR_WRONGPONG @@ -241,8 +241,8 @@ struct RecognizedCommand { 732, IrcCommand::RPL_MONLIST 733, IrcCommand::RPL_ENDOFMONLIS, 2, 2 734, IrcCommand::ERR_MONLISTFULL -740, IrcCommand::RPL_RSACHALLENGE2 -741, IrcCommand::RPL_ENDOFRSACHALLENGE2 +740, IrcCommand::RPL_RSACHALLENGE2, 2, 2 +741, IrcCommand::RPL_ENDOFRSACHALLENGE2, 2, 2 742, IrcCommand::ERR_MLOCKRESTRICTE 743, IrcCommand::ERR_INVALIDBAN 744, IrcCommand::ERR_TOPICLOCK @@ -270,7 +270,7 @@ KICK, IrcCommand::KICK, 3, 3 MODE, IrcCommand::MODE, 2, 15 NICK, IrcCommand::NICK, 1, 1 NOTICE, IrcCommand::NOTICE, 2, 2 -PART, IrcCommand::PART, 2, 2 +PART, IrcCommand::PART, 1, 2 PING, IrcCommand::PING, 1, 1 PRIVMSG, IrcCommand::PRIVMSG, 2, 2 QUIT, IrcCommand::QUIT, 1, 1 diff --git a/main.cpp b/main.cpp index 237f343..d449981 100644 --- a/main.cpp +++ b/main.cpp @@ -12,6 +12,7 @@ #include "ping_thread.hpp" #include "watchdog_thread.hpp" #include "snote_thread.hpp" +#include "self_thread.hpp" #include #include @@ -49,6 +50,31 @@ auto unhandled_message_thread(Connection& connection) -> void }); } +auto echo_thread(Connection& connection) -> void +{ + connection.add_listener([&connection](IrcMsgEvent& event) + { + if (IrcCommand::PRIVMSG == event.command) + { + auto const msg = event.irc.args[1]; + if (msg.starts_with("!raw ")) + { + for (auto const& tag : event.irc.tags) + { + if ("account" == tag.key && "glguy" == tag.val) + { + auto txt = std::string{event.irc.args[1].substr(5)}; + txt += "\r\n"; + connection.write_raw(std::move(txt)); + event.handled_ = true; + return; + } + } + } + } + }); +} + auto start(boost::asio::io_context & io, Settings const& settings) -> void { auto connection = std::make_shared(io); @@ -56,8 +82,10 @@ auto start(boost::asio::io_context & io, Settings const& settings) -> void watchdog_thread(*connection); irc_parse_thread(*connection); ping_thread(*connection); + auto const self_thread = SelfThread::start(*connection); registration_thread(*connection, settings.password, settings.username, settings.realname, settings.nickname); snote_thread(*connection); + echo_thread(*connection); unhandled_message_thread(*connection); boost::asio::co_spawn( diff --git a/self_thread.cpp b/self_thread.cpp new file mode 100644 index 0000000..5a22a70 --- /dev/null +++ b/self_thread.cpp @@ -0,0 +1,76 @@ +#include "self_thread.hpp" + +#include "connection.hpp" +#include "irc_parse_thread.hpp" + +auto SelfThread::start(Connection& connection) -> std::shared_ptr +{ + auto thread = std::make_shared(connection); + + connection.add_listener([thread](IrcMsgEvent& event) + { + switch (event.command) + { + // Learn nickname from 001 + case IrcCommand::RPL_WELCOME: + thread->nickname_ = event.irc.args[0]; + break; + + // Track changes to our nickname + case IrcCommand::NICK: + { + auto const bang = event.irc.source.find('!'); + if (bang != std::string::npos + && thread->nickname_ == event.irc.source.substr(0, bang) + ) + { + thread->nickname_ = event.irc.args[0]; + } + break; + } + + // Re-establish user modes + case IrcCommand::RPL_UMODEIS: + thread->mode_ = event.irc.args[1]; + break; + + // Interpret self mode changes + case IrcCommand::MODE: + if (2 == event.irc.args.size() + && thread->nickname_ == event.irc.args[0] + ) + { + auto polarity = true; + for (char const c : event.irc.args[1]) + { + switch (c) + { + case '+': + polarity = true; + break; + case '-': + polarity = false; + break; + default: + if (polarity) + { + thread->mode_ += c; + } + else + { + auto const ix = thread->mode_.find(c); + if (ix != std::string::npos) + { + thread->mode_.erase(ix, 1); + } + } + break; + } + } + } + default: break; + } + }); + + return thread; +} diff --git a/self_thread.hpp b/self_thread.hpp new file mode 100644 index 0000000..4a29039 --- /dev/null +++ b/self_thread.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +struct Connection; + +class SelfThread +{ + Connection& connection_; + std::string nickname_; + std::string mode_; + +public: + SelfThread(Connection& connection) : connection_{connection} {} + static auto start(Connection&) -> std::shared_ptr; +}; +