initial command logic
This commit is contained in:
parent
093515c3ec
commit
8be5332692
@ -38,12 +38,17 @@ add_custom_command(
|
|||||||
add_subdirectory(mybase64)
|
add_subdirectory(mybase64)
|
||||||
|
|
||||||
add_executable(xbot
|
add_executable(xbot
|
||||||
main.cpp irc_commands.inc ircmsg.cpp settings.cpp connection.cpp
|
main.cpp
|
||||||
registration_thread.cpp
|
irc_commands.inc
|
||||||
snote.cpp
|
bot.cpp
|
||||||
self_thread.cpp
|
connection.cpp
|
||||||
sasl_mechanism.cpp
|
|
||||||
irc_coroutine.cpp
|
irc_coroutine.cpp
|
||||||
|
ircmsg.cpp
|
||||||
|
registration_thread.cpp
|
||||||
|
sasl_mechanism.cpp
|
||||||
|
self_thread.cpp
|
||||||
|
settings.cpp
|
||||||
|
snote.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(xbot PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
target_include_directories(xbot PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
55
bot.cpp
Normal file
55
bot.cpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#include "bot.hpp"
|
||||||
|
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
|
auto Bot::start(std::shared_ptr<SelfThread> self) -> std::shared_ptr<Bot>
|
||||||
|
{
|
||||||
|
const auto thread = std::make_shared<Bot>(std::move(self));
|
||||||
|
|
||||||
|
auto &connection = *thread->self_->get_connection();
|
||||||
|
|
||||||
|
connection.sig_ircmsg.connect([thread](auto cmd, auto &msg) {
|
||||||
|
thread->on_ircmsg(cmd, msg);
|
||||||
|
});
|
||||||
|
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Bot::process_command(std::string_view message, const IrcMsg &msg) -> void
|
||||||
|
{
|
||||||
|
const auto cmdstart = message.find_first_not_of(' ');
|
||||||
|
if (cmdstart == message.npos) return;
|
||||||
|
message = message.substr(cmdstart);
|
||||||
|
|
||||||
|
if (not message.starts_with(command_prefix_)) return;
|
||||||
|
|
||||||
|
auto cmdend = message.find_first_of(' ', 1);
|
||||||
|
|
||||||
|
std::string_view command =
|
||||||
|
cmdend == message.npos ? message : message.substr(0, cmdend);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
std::string_view arguments =
|
||||||
|
cmdend == message.npos ? std::string_view{} : message.substr(cmdend + 1);
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "COMMAND: " << command << " -- [" << arguments << "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Bot::on_ircmsg(IrcCommand cmd, const IrcMsg &msg) -> void
|
||||||
|
{
|
||||||
|
if (cmd == IrcCommand::PRIVMSG)
|
||||||
|
{
|
||||||
|
const auto target = msg.args[0];
|
||||||
|
const auto message = msg.args[1];
|
||||||
|
if (self_->is_my_nick(target))
|
||||||
|
{
|
||||||
|
process_command(message, msg);
|
||||||
|
} else if (self_->is_channel(target)) {
|
||||||
|
const auto colon = message.find(':');
|
||||||
|
if (colon == message.npos) return;
|
||||||
|
if (not self_->is_my_nick(message.substr(0, colon))) return;
|
||||||
|
process_command(message.substr(colon+1), msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
bot.hpp
Normal file
32
bot.hpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "self_thread.hpp"
|
||||||
|
|
||||||
|
#include <boost/signals2.hpp>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
struct Command
|
||||||
|
{
|
||||||
|
std::string_view source;
|
||||||
|
std::string_view target;
|
||||||
|
std::string_view oper;
|
||||||
|
std::string_view account;
|
||||||
|
std::string_view command;
|
||||||
|
std::string_view arguments;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Bot : std::enable_shared_from_this<Bot>
|
||||||
|
{
|
||||||
|
std::shared_ptr<SelfThread> self_;
|
||||||
|
char command_prefix_;
|
||||||
|
|
||||||
|
Bot(std::shared_ptr<SelfThread> self)
|
||||||
|
: self_{std::move(self)}
|
||||||
|
, command_prefix_{'!'}
|
||||||
|
{}
|
||||||
|
|
||||||
|
auto on_ircmsg(IrcCommand, const IrcMsg &) -> void;
|
||||||
|
auto process_command(std::string_view message, const IrcMsg &msg) -> void;
|
||||||
|
static auto start(std::shared_ptr<SelfThread>) -> std::shared_ptr<Bot>;
|
||||||
|
};
|
@ -265,6 +265,11 @@ auto Connection::send_authenticate(std::string_view message) -> void
|
|||||||
write_irc("AUTHENTICATE", message);
|
write_irc("AUTHENTICATE", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Connection::send_join(std::string_view channel) -> void
|
||||||
|
{
|
||||||
|
write_irc("JOIN", channel);
|
||||||
|
}
|
||||||
|
|
||||||
auto Connection::on_authenticate(const std::string_view chunk) -> void
|
auto Connection::on_authenticate(const std::string_view chunk) -> void
|
||||||
{
|
{
|
||||||
if (chunk != "+"sv)
|
if (chunk != "+"sv)
|
||||||
|
@ -33,16 +33,16 @@ private:
|
|||||||
auto watchdog() -> void;
|
auto watchdog() -> void;
|
||||||
auto watchdog_activity() -> void;
|
auto watchdog_activity() -> void;
|
||||||
|
|
||||||
/// Write bytes into the socket. Messages should be properly newline terminated.
|
public:
|
||||||
auto write_line(std::string message) -> void;
|
|
||||||
|
|
||||||
/// Build and send well-formed IRC message from individual parameters
|
/// Build and send well-formed IRC message from individual parameters
|
||||||
auto write_irc(std::string) -> void;
|
auto write_irc(std::string) -> void;
|
||||||
auto write_irc(std::string, std::string_view) -> void;
|
auto write_irc(std::string, std::string_view) -> void;
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
auto write_irc(std::string front, std::string_view next, Args... rest) -> void;
|
auto write_irc(std::string front, std::string_view next, Args... rest) -> void;
|
||||||
|
|
||||||
public:
|
/// Write bytes into the socket.
|
||||||
|
auto write_line(std::string message) -> void;
|
||||||
|
|
||||||
Connection(boost::asio::io_context &io);
|
Connection(boost::asio::io_context &io);
|
||||||
|
|
||||||
boost::signals2::signal<void()> sig_connect;
|
boost::signals2::signal<void()> sig_connect;
|
||||||
@ -71,6 +71,7 @@ public:
|
|||||||
auto send_pass(std::string_view) -> void;
|
auto send_pass(std::string_view) -> void;
|
||||||
auto send_user(std::string_view, std::string_view) -> void;
|
auto send_user(std::string_view, std::string_view) -> void;
|
||||||
auto send_nick(std::string_view) -> void;
|
auto send_nick(std::string_view) -> void;
|
||||||
|
auto send_join(std::string_view) -> void;
|
||||||
auto send_cap_ls() -> void;
|
auto send_cap_ls() -> void;
|
||||||
auto send_cap_end() -> void;
|
auto send_cap_end() -> void;
|
||||||
auto send_cap_req(std::string_view) -> void;
|
auto send_cap_req(std::string_view) -> void;
|
||||||
|
2
main.cpp
2
main.cpp
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "registration_thread.hpp"
|
#include "registration_thread.hpp"
|
||||||
#include "self_thread.hpp"
|
#include "self_thread.hpp"
|
||||||
|
#include "bot.hpp"
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
@ -19,6 +20,7 @@ auto start(boost::asio::io_context &io, const Settings &settings) -> void
|
|||||||
|
|
||||||
const auto selfThread = SelfThread::start(*connection);
|
const auto selfThread = SelfThread::start(*connection);
|
||||||
RegistrationThread::start(*connection, settings, selfThread);
|
RegistrationThread::start(*connection, settings, selfThread);
|
||||||
|
Bot::start(selfThread);
|
||||||
|
|
||||||
connection->sig_snote.connect([](auto &match) {
|
connection->sig_snote.connect([](auto &match) {
|
||||||
std::cout << "SNOTE " << static_cast<int>(match.get_tag()) << std::endl;
|
std::cout << "SNOTE " << static_cast<int>(match.get_tag()) << std::endl;
|
||||||
|
@ -14,6 +14,11 @@ auto SelfThread::on_welcome(const IrcMsg &irc) -> void
|
|||||||
nickname_ = irc.args[0];
|
nickname_ = irc.args[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto SelfThread::on_registered() -> void
|
||||||
|
{
|
||||||
|
connection_.send_join("#lobby");
|
||||||
|
}
|
||||||
|
|
||||||
auto SelfThread::on_nick(const IrcMsg &irc) -> void
|
auto SelfThread::on_nick(const IrcMsg &irc) -> void
|
||||||
{
|
{
|
||||||
if (is_my_mask(irc.source))
|
if (is_my_mask(irc.source))
|
||||||
@ -143,6 +148,9 @@ auto SelfThread::start(Connection &connection) -> std::shared_ptr<SelfThread>
|
|||||||
case IrcCommand::RPL_WELCOME:
|
case IrcCommand::RPL_WELCOME:
|
||||||
thread->on_welcome(msg);
|
thread->on_welcome(msg);
|
||||||
break;
|
break;
|
||||||
|
case IrcCommand::RPL_ENDOFMOTD:
|
||||||
|
thread->on_registered();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -172,7 +180,7 @@ auto SelfThread::get_my_channels() const -> const std::unordered_set<std::string
|
|||||||
|
|
||||||
auto SelfThread::is_my_nick(std::string_view nick) const -> bool
|
auto SelfThread::is_my_nick(std::string_view nick) const -> bool
|
||||||
{
|
{
|
||||||
return nick == nickname_;
|
return casemap_compare(nick, nickname_) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SelfThread::is_my_mask(std::string_view mask) const -> bool
|
auto SelfThread::is_my_mask(std::string_view mask) const -> bool
|
||||||
@ -181,6 +189,11 @@ auto SelfThread::is_my_mask(std::string_view mask) const -> bool
|
|||||||
return bang != std::string_view::npos && nickname_ == mask.substr(0, bang);
|
return bang != std::string_view::npos && nickname_ == mask.substr(0, bang);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto SelfThread::is_channel(std::string_view name) const -> bool
|
||||||
|
{
|
||||||
|
return not name.empty() && channel_prefix_.find(name[0]) != channel_prefix_.npos;
|
||||||
|
}
|
||||||
|
|
||||||
auto SelfThread::on_authenticate(const std::string_view body) -> void
|
auto SelfThread::on_authenticate(const std::string_view body) -> void
|
||||||
{
|
{
|
||||||
if (not sasl_mechanism_)
|
if (not sasl_mechanism_)
|
||||||
@ -213,3 +226,57 @@ auto SelfThread::start_sasl(std::unique_ptr<SaslMechanism> mechanism) -> void
|
|||||||
sasl_mechanism_ = std::move(mechanism);
|
sasl_mechanism_ = std::move(mechanism);
|
||||||
connection_.send_authenticate(sasl_mechanism_->mechanism_name());
|
connection_.send_authenticate(sasl_mechanism_->mechanism_name());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static auto tolower_rfc1459(int c) -> int
|
||||||
|
{
|
||||||
|
return 97 <= c && c <= 126 ? c - 32 : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto tolower_rfc1459_strict(int c) -> int
|
||||||
|
{
|
||||||
|
return 97 <= c && c <= 125 ? c - 32 : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int lower(int)>
|
||||||
|
static auto casemap_impl(std::string_view str) -> std::string
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
result.reserve(str.size());
|
||||||
|
for (auto c : str) {
|
||||||
|
result.push_back(lower(c));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SelfThread::casemap(std::string_view str) const -> std::string
|
||||||
|
{
|
||||||
|
switch (casemap_) {
|
||||||
|
case Casemap::Ascii: return casemap_impl<tolower>(str);
|
||||||
|
case Casemap::Rfc1459: return casemap_impl<tolower_rfc1459>(str);
|
||||||
|
case Casemap::Rfc1459_Strict: return casemap_impl<tolower_rfc1459_strict>(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int lower(int)>
|
||||||
|
static auto casemap_compare_impl(std::string_view lhs, std::string_view rhs) -> int
|
||||||
|
{
|
||||||
|
size_t n1 = lhs.size();
|
||||||
|
size_t n2 = rhs.size();
|
||||||
|
size_t n = std::min(n1, n2);
|
||||||
|
for (size_t i = 0; i < n; ++i) {
|
||||||
|
if (lower(lhs[i]) < lower(rhs[i])) return -1;
|
||||||
|
if (lower(lhs[i]) > lower(rhs[i])) return 1;
|
||||||
|
}
|
||||||
|
if (n1 < n2) return -1;
|
||||||
|
if (n1 > n2) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SelfThread::casemap_compare(std::string_view lhs, std::string_view rhs) const -> int
|
||||||
|
{
|
||||||
|
switch (casemap_) {
|
||||||
|
case Casemap::Ascii: return casemap_compare_impl<tolower>(lhs, rhs);
|
||||||
|
case Casemap::Rfc1459: return casemap_compare_impl<tolower_rfc1459>(lhs, rhs);
|
||||||
|
case Casemap::Rfc1459_Strict: return casemap_compare_impl<tolower_rfc1459_strict>(lhs, rhs);
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,13 @@
|
|||||||
struct Connection;
|
struct Connection;
|
||||||
struct IrcMsg;
|
struct IrcMsg;
|
||||||
|
|
||||||
|
enum class Casemap
|
||||||
|
{
|
||||||
|
Rfc1459,
|
||||||
|
Rfc1459_Strict,
|
||||||
|
Ascii,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Thread to track this connection's identity, and IRC state.
|
* @brief Thread to track this connection's identity, and IRC state.
|
||||||
*
|
*
|
||||||
@ -23,9 +30,11 @@ class SelfThread
|
|||||||
|
|
||||||
// RPL_ISUPPORT state
|
// RPL_ISUPPORT state
|
||||||
std::unordered_map<std::string, std::string> isupport_;
|
std::unordered_map<std::string, std::string> isupport_;
|
||||||
|
|
||||||
std::unique_ptr<SaslMechanism> sasl_mechanism_;
|
std::unique_ptr<SaslMechanism> sasl_mechanism_;
|
||||||
|
|
||||||
|
Casemap casemap_;
|
||||||
|
std::string channel_prefix_;
|
||||||
|
|
||||||
auto on_welcome(const IrcMsg &irc) -> void;
|
auto on_welcome(const IrcMsg &irc) -> void;
|
||||||
auto on_isupport(const IrcMsg &irc) -> void;
|
auto on_isupport(const IrcMsg &irc) -> void;
|
||||||
auto on_nick(const IrcMsg &irc) -> void;
|
auto on_nick(const IrcMsg &irc) -> void;
|
||||||
@ -35,20 +44,32 @@ class SelfThread
|
|||||||
auto on_part(const IrcMsg &irc) -> void;
|
auto on_part(const IrcMsg &irc) -> void;
|
||||||
auto on_mode(const IrcMsg &irc) -> void;
|
auto on_mode(const IrcMsg &irc) -> void;
|
||||||
auto on_authenticate(std::string_view) -> void;
|
auto on_authenticate(std::string_view) -> void;
|
||||||
|
auto on_registered() -> void;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SelfThread(Connection &connection)
|
SelfThread(Connection &connection)
|
||||||
: connection_{connection}
|
: connection_{connection}
|
||||||
|
, casemap_{Casemap::Rfc1459}
|
||||||
|
, channel_prefix_{"#&"}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
static auto start(Connection &) -> std::shared_ptr<SelfThread>;
|
static auto start(Connection &) -> std::shared_ptr<SelfThread>;
|
||||||
|
|
||||||
auto start_sasl(std::unique_ptr<SaslMechanism> mechanism) -> void;
|
auto start_sasl(std::unique_ptr<SaslMechanism> mechanism) -> void;
|
||||||
|
|
||||||
|
auto get_connection() const -> std::shared_ptr<Connection>
|
||||||
|
{
|
||||||
|
return connection_.shared_from_this();
|
||||||
|
}
|
||||||
|
|
||||||
auto get_my_nickname() const -> const std::string &;
|
auto get_my_nickname() const -> const std::string &;
|
||||||
auto get_my_mode() const -> const std::string &;
|
auto get_my_mode() const -> const std::string &;
|
||||||
auto get_my_channels() const -> const std::unordered_set<std::string> &;
|
auto get_my_channels() const -> const std::unordered_set<std::string> &;
|
||||||
|
|
||||||
auto is_my_nick(std::string_view nick) const -> bool;
|
auto is_my_nick(std::string_view nick) const -> bool;
|
||||||
auto is_my_mask(std::string_view nick) const -> bool;
|
auto is_my_mask(std::string_view mask) const -> bool;
|
||||||
|
auto is_channel(std::string_view name) const -> bool;
|
||||||
|
|
||||||
|
auto casemap(std::string_view) const -> std::string;
|
||||||
|
auto casemap_compare(std::string_view, std::string_view) const -> int;
|
||||||
};
|
};
|
||||||
|
@ -34,16 +34,17 @@ enum class SnoteTag
|
|||||||
class SnoteMatch
|
class SnoteMatch
|
||||||
{
|
{
|
||||||
SnoteTag tag_;
|
SnoteTag tag_;
|
||||||
std::variant<std::pair<std::regex const&, std::string_view>, std::match_results<std::string_view::const_iterator>> components_;
|
std::variant<std::pair<const std::regex &, std::string_view>, std::match_results<std::string_view::const_iterator>> components_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SnoteMatch(SnoteTag tag, std::regex const& regex, std::string_view full)
|
SnoteMatch(SnoteTag tag, const std::regex ®ex, std::string_view full)
|
||||||
: tag_{tag}
|
: tag_{tag}
|
||||||
, components_{std::make_pair(std::ref(regex), full)}
|
, components_{std::make_pair(std::ref(regex), full)}
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
auto get_tag() -> SnoteTag { return tag_; }
|
auto get_tag() -> SnoteTag { return tag_; }
|
||||||
auto get_results() -> std::match_results<std::string_view::const_iterator> const&;
|
auto get_results() -> const std::match_results<std::string_view::const_iterator> &;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SnoteCore
|
struct SnoteCore
|
||||||
|
Loading…
x
Reference in New Issue
Block a user