xbot/registration_thread.cpp
2023-11-29 13:54:34 -08:00

183 lines
4.3 KiB
C++

#include "registration_thread.hpp"
#include "connection.hpp"
#include "irc_parse_thread.hpp"
#include "ircmsg.hpp"
#include "write_irc.hpp"
#include <memory>
#include <unordered_map>
#include <unordered_set>
RegistrationThread::RegistrationThread(
Connection& connection,
std::string password,
std::string username,
std::string realname,
std::string nickname
)
: connection_{connection}
, password_{password}
, username_{username}
, realname_{realname}
, nickname_{nickname}
{
}
auto RegistrationThread::on_connect() -> void
{
send_cap_ls(connection_);
send_pass(connection_, password_);
send_user(connection_, username_, realname_);
send_nick(connection_, nickname_);
connection_.remove_listener(connect_handle_);
}
auto RegistrationThread::send_req() -> void
{
std::string request;
char const* const want[] = {
"account-notify",
"account-tag",
"batch",
"chghost",
"draft/chathistory",
"extended-join",
"invite-notify",
"server-time",
"setname",
"soju.im/no-implicit-names",
"solanum.chat/identify-msg",
"solanum.chat/oper",
"solanum.chat/realhost",
};
for (auto cap : want)
{
if (caps.contains(cap))
{
request.append(cap);
request.push_back(' ');
outstanding.insert(cap);
}
}
connection_.remove_listener(message_handle_);
if (not outstanding.empty())
{
request.pop_back();
send_cap_req(connection_, request);
listen_for_cap_ack();
}
else
{
send_cap_end(connection_);
}
}
auto RegistrationThread::on_msg_cap_ack(IrcMsg const& msg) -> void
{
auto in = std::istringstream{std::string{msg.args[2]}};
std::for_each(
std::istream_iterator<std::string>{in},
std::istream_iterator<std::string>{},
[this](std::string x) {
outstanding.erase(x);
}
);
if (outstanding.empty())
{
send_cap_end(connection_);
connection_.remove_listener(message_handle_);
}
}
auto RegistrationThread::on_msg_cap_ls(IrcMsg const& msg) -> void
{
std::string_view const* kvs;
bool last;
if (3 == msg.args.size())
{
kvs = &msg.args[2];
last = true;
}
else if (4 == msg.args.size() && "*" == msg.args[2])
{
kvs = &msg.args[3];
last = false;
}
else
{
return;
}
auto in = std::istringstream{std::string{*kvs}};
std::for_each(
std::istream_iterator<std::string>{in},
std::istream_iterator<std::string>{},
[this](std::string x) {
auto const eq = x.find('=');
if (eq == x.npos)
{
caps.emplace(x, std::string{});
}
else
{
caps.emplace(std::string{x, 0, eq}, std::string{x, eq+1, x.npos});
}
}
);
if (last)
{
send_req();
}
}
auto RegistrationThread::start(
Connection& connection,
std::string password,
std::string username,
std::string realname,
std::string nickname
) -> std::shared_ptr<RegistrationThread>
{
auto const thread = std::make_shared<RegistrationThread>(connection, password, username, realname, nickname);
thread->listen_for_cap_ls();
thread->connect_handle_ = connection.add_listener<ConnectEvent>([thread](ConnectEvent const&)
{
thread->on_connect();
});
return thread;
}
auto RegistrationThread::listen_for_cap_ack() -> void
{
message_handle_ = connection_.add_listener<IrcMsgEvent>([thread = shared_from_this()](IrcMsgEvent const& event)
{
if (IrcCommand::CAP == event.command && event.irc.args.size() >= 2 && "*" == event.irc.args[0] && "ACK" == event.irc.args[1])
{
thread->on_msg_cap_ack(event.irc);
}
});
}
auto RegistrationThread::listen_for_cap_ls() -> void
{
message_handle_ = connection_.add_listener<IrcMsgEvent>([thread = shared_from_this()](IrcMsgEvent const& event)
{
if (IrcCommand::CAP == event.command && event.irc.args.size() >= 2 && "*" == event.irc.args[0] && "LS" == event.irc.args[1])
{
thread->on_msg_cap_ls(event.irc);
}
});
}