mirror of
https://gitlab.com/niansa/asbots.git
synced 2025-03-06 20:48:25 +01:00
Even more improvements lol
This commit is contained in:
parent
d2f38b272b
commit
315b65f0c3
9 changed files with 178 additions and 85 deletions
21
exceptions.hpp
Normal file
21
exceptions.hpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#ifndef __EXCEPTIONS_HPP
|
||||
#define __EXCEPTIONS_HPP
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
|
||||
struct ParseError : public std::runtime_error {
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
struct ConnectionError : public std::runtime_error {
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
struct DesyncError : public std::exception {
|
||||
const char *what() const throw() {
|
||||
return "Server has desynced!!!";
|
||||
}
|
||||
DesyncError() {
|
||||
std::cout << "DESCONACCREKO" << std::endl;
|
||||
}
|
||||
};
|
||||
#endif
|
13
incompletes.hpp
Normal file
13
incompletes.hpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef _INCOMPLETES_HPP
|
||||
#define _INCOMPLETES_HPP
|
||||
#include <memory>
|
||||
|
||||
|
||||
|
||||
struct User;
|
||||
struct Channel;
|
||||
struct Cache;
|
||||
struct NetworkInfo;
|
||||
using u_User = std::unique_ptr<User>;
|
||||
using u_Channel = std::unique_ptr<Channel>;
|
||||
#endif
|
101
instance.cpp
101
instance.cpp
|
@ -36,7 +36,7 @@ void Event::parse(std::string_view str) {
|
|||
name = std::move(split[1]);
|
||||
if (split.size() == 3) {
|
||||
// Get args and text
|
||||
std::tie(args, text) = colonSplit(split[2]);
|
||||
std::tie(raw_args, text) = colonSplit(split[2]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,19 +112,22 @@ void ModeSet::parse(std::string_view in, NetworkInfo &netInfo) {
|
|||
|
||||
void User::parse_euid(const Event& event, NetworkInfo& netInfo) {
|
||||
this->server = std::get<SUID>(event.sender.id);
|
||||
auto split = strSplit(event.args, ' ', 8);
|
||||
// Check size
|
||||
if (split.size() != 9) {
|
||||
if (event.args.size() < 10) {
|
||||
throw ParseError("In euid parser: This euid event does not have enough arguments");
|
||||
}
|
||||
// Move values
|
||||
nick = std::move(split[0]);
|
||||
hops = std::stoull(std::string(split[1]));
|
||||
umode.parse<false>(split[3], netInfo);
|
||||
ident = std::move(split[4]);
|
||||
host = std::move(split[5]);
|
||||
realhost = std::move(split[6]);
|
||||
uid = UUID(std::move(split[7]));
|
||||
nick = event.args[0];
|
||||
hops = std::stoull(std::string(event.args[1]));
|
||||
umode.parse<false>(event.args[3], netInfo);
|
||||
ident = event.args[4];
|
||||
vhost = event.args[5];
|
||||
ip = event.args[6];
|
||||
uid = UUID(event.args[7]);
|
||||
realhost = event.args[8];
|
||||
if (event.args[9] != "*" && event.args[9] != "0") {
|
||||
loginName = event.args[9];
|
||||
}
|
||||
// Get realname
|
||||
realname = std::move(event.text);
|
||||
}
|
||||
|
@ -136,14 +139,13 @@ void User::removeChannel(const u_Channel& channel) {
|
|||
|
||||
void Channel::parse_sjoin(const Event& event, Cache& cache, NetworkInfo& netInfo) {
|
||||
this->server = std::get<SUID>(event.sender.id);
|
||||
auto split = strSplit(event.args, ' ', 2);
|
||||
// Check size
|
||||
if (split.size() != 3) {
|
||||
if (event.args.size() < 3) {
|
||||
throw ParseError("In euid parser: This euid event does not have enough arguments");
|
||||
}
|
||||
// Move values
|
||||
name = std::move(split[1]);
|
||||
mode.parse<true>(split[2], netInfo);
|
||||
name = std::move(event.args[1]);
|
||||
mode.parse<true>(event.args[2], netInfo);
|
||||
// Get members
|
||||
for (auto& raw_uuid : strSplit(event.text, ' ')) {
|
||||
// Erase prefix
|
||||
|
@ -302,23 +304,21 @@ async::result<void> Instance::process(const Command command) {
|
|||
}
|
||||
}
|
||||
|
||||
async::result<void> Instance::process(const Event event) {
|
||||
async::result<void> Instance::process(Event event) {
|
||||
event.splitArgs();
|
||||
std::clog << event.dump() << std::flush;
|
||||
if (event.name == "NOTICE") {
|
||||
// Don't do anything special
|
||||
} else if (!authed) {
|
||||
if (!authed && event.name != "NOTICE") {
|
||||
throw ConnectionError("Server tried to send an event before authenticating");
|
||||
}
|
||||
// Fetched info
|
||||
else if (event.name == "005") {
|
||||
// Check if that 005 was for me
|
||||
if (event.args.find(config.get().server.uid.str()) != 0) {
|
||||
if (event.args[0] != config.get().server.uid.str()) {
|
||||
co_return;
|
||||
}
|
||||
// Split the list
|
||||
auto split = strSplit(event.args, ' ');
|
||||
// Iterate and find the information we need
|
||||
for (const auto& field : split) {
|
||||
for (const auto& field : event.args) {
|
||||
// Split into key and value
|
||||
auto split = strSplit(field, '=', 1);
|
||||
// Check size
|
||||
|
@ -389,7 +389,7 @@ async::result<void> Instance::process(const Event event) {
|
|||
throw DesyncError();
|
||||
}
|
||||
// Set nick
|
||||
res->get()->nick = strSplit(event.args, ' ', 1)[0];
|
||||
res->get()->nick = event.args[0];
|
||||
} else if (event.name == "MODE") {
|
||||
// User changed their mode
|
||||
// Find user in cache
|
||||
|
@ -399,6 +399,34 @@ async::result<void> Instance::process(const Event event) {
|
|||
}
|
||||
// Update mode
|
||||
res->get()->umode.parse<false>(event.text, netInfo);
|
||||
} else if (event.name == "ENCAP") {
|
||||
// Get args
|
||||
if (event.args.size() < 3) {
|
||||
throw ParseError("In encap event parser: at least * and encap name need to be passed");
|
||||
}
|
||||
if (event.args[1] == "SU") {
|
||||
// User logged in
|
||||
// Check args
|
||||
if (event.args.size() < 5) {
|
||||
throw ParseError("In encap su event parser: this encap requires UID as first argument and optionally login name as second one");
|
||||
}
|
||||
// Find user in cache
|
||||
auto res = cache.find_user_by_uid(event.args[2]);
|
||||
if (res == cache.users.end()) {
|
||||
throw DesyncError();
|
||||
}
|
||||
// Update login name
|
||||
if (event.args.size() > 3) {
|
||||
auto loginName = event.args[3];
|
||||
if (loginName == "*" || loginName == "0") {
|
||||
res->get()->loginName.reset();
|
||||
} else {
|
||||
res->get()->loginName = event.args[3];
|
||||
}
|
||||
} else {
|
||||
res->get()->loginName.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Channel updates
|
||||
else if (event.name == "SJOIN") {
|
||||
|
@ -411,7 +439,7 @@ async::result<void> Instance::process(const Event event) {
|
|||
} else if (event.name == "TOPIC" || event.name == "TB") {
|
||||
// Channels topic changed
|
||||
// Find channel in cache
|
||||
auto res = cache.find_channel_by_name(strSplit(event.args, ' ', 1)[0]);
|
||||
auto res = cache.find_channel_by_name(event.args[0]);
|
||||
if (res == cache.channels.end()) {
|
||||
throw DesyncError();
|
||||
}
|
||||
|
@ -420,12 +448,11 @@ async::result<void> Instance::process(const Event event) {
|
|||
} else if (event.name == "JOIN") {
|
||||
// User joined existing channel
|
||||
// Split args
|
||||
auto split = strSplit(event.args, ' ', 2);
|
||||
if (split.size() != 3) {
|
||||
if (event.args.size() < 3) {
|
||||
throw ParseError("In join event parser: join even did not receive enough arguments (expected 3)");
|
||||
}
|
||||
// Get channel from cache
|
||||
auto c_res = cache.find_channel_by_name(split[1]);
|
||||
auto c_res = cache.find_channel_by_name(event.args[1]);
|
||||
if (c_res == cache.channels.end()) {
|
||||
throw DesyncError();
|
||||
}
|
||||
|
@ -438,17 +465,16 @@ async::result<void> Instance::process(const Event event) {
|
|||
c_res->get()->members.push_back(&*u_res);
|
||||
u_res->get()->channels.push_back(&*c_res);
|
||||
// Update channel modes
|
||||
c_res->get()->mode.parse<true>(split[2], netInfo);
|
||||
c_res->get()->mode.parse<true>(event.args[2], netInfo);
|
||||
} else if (event.name == "PART" || event.name == "KICK") {
|
||||
// User left channel
|
||||
auto split = strSplit(event.args, ' ', 1);
|
||||
// Get channel from cache
|
||||
auto c_res = cache.find_channel_by_name(split[0]);
|
||||
auto c_res = cache.find_channel_by_name(event.args[0]);
|
||||
if (c_res == cache.channels.end()) {
|
||||
throw DesyncError();
|
||||
}
|
||||
// Get user from cache
|
||||
auto u_res = cache.find_user_by_uid((event.name=="PART")?std::get<UUID>(event.sender.id):split[1]);
|
||||
auto u_res = cache.find_user_by_uid((event.name=="PART")?std::get<UUID>(event.sender.id):event.args[1]);
|
||||
if (u_res == cache.users.end()) {
|
||||
throw DesyncError();
|
||||
}
|
||||
|
@ -460,12 +486,11 @@ async::result<void> Instance::process(const Event event) {
|
|||
// Split args
|
||||
std::string_view modes, channelName;
|
||||
{
|
||||
auto split = strSplit(event.args, ' ', 2);
|
||||
if (split.size() != 3) {
|
||||
throw DesyncError();
|
||||
if (event.args.size() < 3) {
|
||||
throw ParseError("In MODE/BMASK event parser: did not receive enough arguments (expected 3)");
|
||||
}
|
||||
channelName = split[1];
|
||||
modes = split[2];
|
||||
channelName = event.args[1];
|
||||
modes = event.args[2];
|
||||
}
|
||||
// Get channel from cache
|
||||
auto c_res = cache.find_channel_by_name(channelName);
|
||||
|
@ -479,18 +504,18 @@ async::result<void> Instance::process(const Event event) {
|
|||
else if (event.name == "PRIVMSG") {
|
||||
// On message
|
||||
// Check that message is not directed to channel
|
||||
if (isalnum(event.args[0])) {
|
||||
if (isalnum(event.raw_args[0])) {
|
||||
// Get author from cache
|
||||
auto res = cache.find_user_by_uid(std::get<UUID>(event.sender.id));
|
||||
if (res == cache.users.end()) {
|
||||
throw DesyncError();
|
||||
}
|
||||
// Get target UUID
|
||||
UUID target(event.args);
|
||||
UUID target(event.args[0]);
|
||||
// Pass to services with ownership over target
|
||||
for (auto& service : services) {
|
||||
if (service->ready && service->uuid.str() == target.str()) {
|
||||
co_await service->on_direct_privmsg(event.text, *res->get());
|
||||
co_await service->on_direct_privmsg(event.text, *res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
87
instance.hpp
87
instance.hpp
|
@ -3,6 +3,9 @@
|
|||
#include "config.hpp"
|
||||
#include "uid.hpp"
|
||||
#include "serviceBase.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "incompletes.hpp"
|
||||
#include "exceptions.hpp"
|
||||
|
||||
#include <uvpp.hpp>
|
||||
#include <async/result.hpp>
|
||||
|
@ -11,44 +14,26 @@
|
|||
#include <fmt/format.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <ctime>
|
||||
|
||||
|
||||
|
||||
struct ParseError : public std::runtime_error {
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
struct ConnectionError : public std::runtime_error {
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
struct DesyncError : public std::exception {
|
||||
const char *what() const throw() {
|
||||
return "Server has desynced!!!";
|
||||
}
|
||||
DesyncError() {
|
||||
std::cout << "DESCONACCREKO" << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
struct User;
|
||||
struct Channel;
|
||||
struct Cache;
|
||||
struct NetworkInfo;
|
||||
using u_User = std::unique_ptr<User>;
|
||||
using u_Channel = std::unique_ptr<Channel>;
|
||||
|
||||
struct Event {
|
||||
struct Event { // DON'T std::move() this class. EVER!!!
|
||||
AnyUID sender;
|
||||
std::string name, args, text;
|
||||
std::string name, raw_args, text;
|
||||
std::vector<std::string_view> args;
|
||||
|
||||
std::string dump() const {
|
||||
return fmt::format(":{} {} {}{}\n", sender.str(), name, args, (text.empty()?"":" :"+text));
|
||||
return fmt::format(":{} {} {}{}\n", sender.str(), name, raw_args, (text.empty()?"":" :"+text));
|
||||
}
|
||||
void parse(std::string_view str);
|
||||
void splitArgs() {
|
||||
args = Utility::strSplit(raw_args, ' ');
|
||||
}
|
||||
};
|
||||
|
||||
struct Command {
|
||||
|
@ -82,10 +67,18 @@ struct Channel {
|
|||
return {
|
||||
.sender = SUID(server),
|
||||
.name = "SJOIN",
|
||||
.args = fmt::format("{} {} +{}", time(nullptr), name, mode.str),
|
||||
.raw_args = fmt::format("{} {} +{}", time(nullptr), name, mode.str),
|
||||
.text = fmt::format("@{}", initial_user.str())
|
||||
};
|
||||
}
|
||||
Event get_topic(std::string_view topic, const UUID& user) const {
|
||||
return {
|
||||
.sender = user,
|
||||
.name = "TOPIC",
|
||||
.raw_args = name,
|
||||
.text = std::string(topic)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct User {
|
||||
|
@ -94,11 +87,13 @@ struct User {
|
|||
size_t hops = 1;
|
||||
ModeSet umode;
|
||||
std::string ident;
|
||||
std::string host;
|
||||
std::optional<std::string> vhost;
|
||||
std::optional<std::string> ip;
|
||||
std::string realhost;
|
||||
UUID uid;
|
||||
std::string realname;
|
||||
std::vector<u_Channel*> channels;
|
||||
std::optional<std::string> loginName;
|
||||
|
||||
void parse_euid(const Event &event, NetworkInfo& netInfo);
|
||||
void removeChannel(const u_Channel &channel);
|
||||
|
@ -107,21 +102,32 @@ struct User {
|
|||
return {
|
||||
.sender = SUID(server),
|
||||
.name = "EUID",
|
||||
.args = fmt::format("{} 1 {} +{} {} {} 0 {} * * :{}", nick, time(nullptr), umode.str, ident, host, uid.str(), realname)
|
||||
// nick
|
||||
// | hops
|
||||
// | | ts
|
||||
// | | | umode
|
||||
// | | | | ident
|
||||
// | | | | | vhost
|
||||
// | | | | | | ip
|
||||
// | | | | | | | uid
|
||||
// | | | | | | | | realhost
|
||||
// | | | | | | | | | account
|
||||
// | | | | | | | | | | realname
|
||||
.raw_args = fmt::format("{} 1 {} +{} {} {} {} {} {} {} :{}", nick, time(nullptr), umode.str, ident, vhost.value_or(realhost), ip.value_or("*"), uid.str(), realhost, loginName.value_or("*"), realname)
|
||||
};
|
||||
}
|
||||
Event get_join(std::string_view channelName) const {
|
||||
return {
|
||||
.sender = uid,
|
||||
.name = "JOIN",
|
||||
.args = fmt::format("{} {} +", time(nullptr), channelName)
|
||||
.raw_args = fmt::format("{} {} +", time(nullptr), channelName)
|
||||
};
|
||||
}
|
||||
Event get_part(std::string_view channelName, std::string_view message = "") const {
|
||||
return {
|
||||
.sender = uid,
|
||||
.name = "PART",
|
||||
.args = std::string(channelName),
|
||||
.raw_args = std::string(channelName),
|
||||
.text = std::string(message)
|
||||
};
|
||||
}
|
||||
|
@ -133,19 +139,28 @@ struct User {
|
|||
};
|
||||
}
|
||||
|
||||
Event get_privmsg(std::string_view message, AnyUID target) {
|
||||
Event get_encap_su(std::string_view login_name) {
|
||||
//:<my_sid> ENCAP * SU <user_uid> <new_account_name>
|
||||
return {
|
||||
.sender = server,
|
||||
.name = "ENCAP",
|
||||
.raw_args = fmt::format("* SU {} {}", uid.str(), login_name)
|
||||
};
|
||||
}
|
||||
|
||||
Event get_privmsg(std::string_view message, AnyUID target) const {
|
||||
return {
|
||||
.sender = uid,
|
||||
.name = "PRIVMSG",
|
||||
.args = std::string(target.str()),
|
||||
.raw_args = std::string(target.str()),
|
||||
.text = std::string(message)
|
||||
};
|
||||
}
|
||||
Event get_notice(std::string_view message, AnyUID target) {
|
||||
Event get_notice(std::string_view message, AnyUID target) const {
|
||||
return {
|
||||
.sender = uid,
|
||||
.name = "NOTICE",
|
||||
.args = std::string(target.str()),
|
||||
.raw_args = std::string(target.str()),
|
||||
.text = std::string(message)
|
||||
};
|
||||
}
|
||||
|
@ -227,7 +242,7 @@ public:
|
|||
async::result<void> login();
|
||||
async::result<void> burst();
|
||||
async::result<void> process(const Command);
|
||||
async::result<void> process(const Event);
|
||||
async::result<void> process(Event);
|
||||
|
||||
async::result<void> send_event(const Event&);
|
||||
async::result<void> send_ping();
|
||||
|
|
|
@ -5,6 +5,7 @@ class User;
|
|||
class Instance;
|
||||
|
||||
#include "uid.hpp"
|
||||
#include "incompletes.hpp"
|
||||
|
||||
#include <async/result.hpp>
|
||||
|
||||
|
@ -18,7 +19,7 @@ public:
|
|||
|
||||
virtual async::result<void> intitialize() = 0;
|
||||
virtual async::result<void> on_event(const Event& event) = 0;
|
||||
virtual async::result<void> on_direct_privmsg(std::string_view msg, const User& author) = 0;
|
||||
virtual async::result<void> on_direct_privmsg(std::string_view msg, u_User& author) = 0;
|
||||
|
||||
async::result<void> mark_ready(const User& user);
|
||||
};
|
||||
|
|
|
@ -23,6 +23,6 @@ async::result<void> TestService::on_event(const Event& event) {
|
|||
co_return;
|
||||
}
|
||||
|
||||
async::result<void> TestService::on_direct_privmsg(std::string_view msg, const User& author) {
|
||||
async::result<void> TestService::on_direct_privmsg(std::string_view msg, u_User& author) {
|
||||
co_await i->send_event(user.get_privmsg("I received a message: "+std::string(msg), "#lol"));
|
||||
}
|
||||
|
|
|
@ -12,6 +12,6 @@ class TestService : public ServiceBase {
|
|||
|
||||
virtual async::result<void> intitialize() override;
|
||||
virtual async::result<void> on_event(const Event& event) override;
|
||||
virtual async::result<void> on_direct_privmsg(std::string_view msg, const User& author) override;
|
||||
virtual async::result<void> on_direct_privmsg(std::string_view msg, u_User& author) override;
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "test.hpp"
|
||||
#include "../utility.hpp"
|
||||
|
||||
#include <async/result.hpp>
|
||||
#include <fmt/format.h>
|
||||
|
@ -16,18 +17,19 @@ async::result<void> TestService::intitialize() {
|
|||
.server = i->config.get().server.uid,
|
||||
.nick = "services",
|
||||
.ident = "services",
|
||||
.host = "services.",
|
||||
.realhost = "127.0.0.1",
|
||||
.realhost = "services.",
|
||||
.uid = uuid,
|
||||
.realname = i->netInfo.name
|
||||
.realname = i->netInfo.name,
|
||||
.loginName = "serviceD"
|
||||
};
|
||||
Channel serviceChannel = {
|
||||
.server = i->config.get().server.uid,
|
||||
.name = serviceChannelName
|
||||
};
|
||||
co_await mark_ready(user);
|
||||
co_await i->send_event(serviceChannel.get_sjoin(uuid));
|
||||
co_await i->send_event(user.get_join(serviceChannelName));
|
||||
co_await i->send_event(serviceChannel.get_sjoin(user.uid));
|
||||
co_await i->send_event(serviceChannel.get_topic("IRC Service channel", user.uid));
|
||||
//co_await i->send_event(user.get_encap_su("serviceD")); DOES NOT YET WORK!!
|
||||
co_await i->send_event(user.get_notice("Test... Hello world!", serviceChannelName));
|
||||
}
|
||||
|
||||
|
@ -35,6 +37,22 @@ async::result<void> TestService::on_event(const Event& event) {
|
|||
co_return;
|
||||
}
|
||||
|
||||
async::result<void> TestService::on_direct_privmsg(std::string_view msg, const User& author) {
|
||||
co_await i->send_event(user.get_privmsg("I received a message from {}: {}"_format(author.nick, msg), serviceChannelName));
|
||||
async::result<void> TestService::on_direct_privmsg(std::string_view msg, u_User &author) {
|
||||
auto cmd = Utility::strSplit(msg, ' ', 1);
|
||||
Event response = user.get_notice("You need to log in first", author->uid);
|
||||
if (cmd[0] == "SEND") {
|
||||
if (author->loginName.value_or("") == "NetOP") {
|
||||
Event event;
|
||||
try {
|
||||
event.parse(cmd[1]);
|
||||
co_await i->send_event(event);
|
||||
response = user.get_notice("Sent.", author->uid);
|
||||
} catch (std::exception&e) {
|
||||
response = user.get_notice(e.what(), author->uid);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
response = user.get_notice("Unknown command", author->uid);
|
||||
}
|
||||
co_await i->send_event(response);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,6 @@ class TestService : public ServiceBase {
|
|||
|
||||
virtual async::result<void> intitialize() override;
|
||||
virtual async::result<void> on_event(const Event& event) override;
|
||||
virtual async::result<void> on_direct_privmsg(std::string_view msg, const User& author) override;
|
||||
virtual async::result<void> on_direct_privmsg(std::string_view msg, u_User& author) override;
|
||||
};
|
||||
#endif
|
||||
|
|
Loading…
Add table
Reference in a new issue