1
0
Fork 0
mirror of https://gitlab.com/niansa/asbots.git synced 2025-03-06 20:48:25 +01:00
asbots/instance.hpp
2021-06-28 13:26:56 +02:00

295 lines
9.2 KiB
C++

/*
* asbots
* Copyright (C) 2021 niansa
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef _INSTANCE_HPP
#define _INSTANCE_HPP
#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>
#include <async/oneshot-event.hpp>
#include <frg/std_compat.hpp>
#include <fmt/format.h>
#include <string>
#include <string_view>
#include <sstream>
#include <optional>
#include <unordered_map>
#include <vector>
#include <memory>
#include <ctime>
struct Event { // DON'T std::move() this class. EVER!!!
AnyUID sender;
std::string name, raw_args, text;
std::vector<std::string_view> args;
std::string dump() const {
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, ' ');
}
std::string_view fromArgToEnd(std::string_view arg) {
return {arg.data(), raw_args.size() - (arg.data() - raw_args.data())};
}
};
struct Command {
std::string name, args, text;
std::string dump() const {
return fmt::format("{} {}{}\n", name, args, (text.empty()?"":" :"+text));
}
void parse(std::string_view str);
};
struct ModeSet {
std::string str;
std::vector<std::tuple<char, std::string>> params;
std::string fullStr() const {
std::ostringstream o;
o << str;
for (const auto& [mode, param] : params) {
o << ' ' << param;
}
return o.str();
}
bool listModeExists(char character, std::string_view value) {
return std::find_if(params.begin(), params.end(), [&] (const auto o) {
return std::get<0>(o) == character && std::get<1>(o) == value;
}) != params.end();
}
template<bool channelModes>
void parse(std::string_view str, NetworkInfo& netInfo);
};
struct Channel {
SUID server;
std::string name;
ModeSet mode;
time_t timeStamp = 0;
std::string topic;
std::vector<User*> members;
void parse_sjoin(const Event &event, Cache& cache, NetworkInfo& netInfo);
void removeMember(const User *member);
Event get_sjoin(const UUID& initial_user, const SUID& my_sid) const {
return {
.sender = my_sid,
.name = "SJOIN",
.raw_args = fmt::format("{} {} +", timeStamp?timeStamp:time(nullptr), name),
.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)
};
}
Event get_tmode(const ModeSet& modes, const AnyUID& sender) const {
return {
.sender = AnyUID(sender),
.name = "TMODE",
.raw_args = fmt::format("{} {} +{}", timeStamp?timeStamp:time(nullptr), name, modes.fullStr())
};
}
};
struct User {
SUID server;
std::string nick;
size_t hops = 1;
ModeSet umode;
std::string ident = "~";
std::optional<std::string> vhost;
std::optional<std::string> ip;
std::string realhost;
UUID uid;
std::string realname;
std::vector<Channel*> channels;
std::optional<std::string> loginName;
void parse_euid(const Event &event, NetworkInfo& netInfo);
void removeChannel(const Channel *channel);
Event get_euid() const {
return {
.sender = SUID(server),
.name = "EUID",
// 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",
.raw_args = fmt::format("{} {} +", time(nullptr), channelName)
};
}
Event get_part(std::string_view channelName, std::string_view message = "") const {
return {
.sender = uid,
.name = "PART",
.raw_args = std::string(channelName),
.text = std::string(message)
};
}
Event get_quit(std::string_view message = "") const {
return {
.sender = uid,
.name = "QUIT",
.text = std::string(message)
};
}
Event get_encap_su(std::string_view login_name, const SUID& my_sid) {
//:<my_sid> ENCAP * SU <user_uid> <new_account_name>
return {
.sender = my_sid,
.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",
.raw_args = std::string(target.str()),
.text = std::string(message)
};
}
Event get_notice(std::string_view message, AnyUID target) const {
return {
.sender = uid,
.name = "NOTICE",
.raw_args = std::string(target.str()),
.text = std::string(message)
};
}
};
struct Cache {
std::vector<std::unique_ptr<User>> users;
std::vector<std::unique_ptr<Channel>> channels;
std::vector<std::unique_ptr<User>>::iterator find_user_by_nick(std::string_view nick);
std::vector<std::unique_ptr<User>>::iterator find_user_by_uid(const UUID& uid);
std::vector<std::unique_ptr<Channel>>::iterator find_channel_by_name(std::string_view name);
};
struct NetworkInfo {
async::oneshot_event ready_event;
size_t fields_received = 0;
bool ready = false;
std::string name;
struct {
// list modes first, then modes that take a param when setting but not when unsetting (just +k), then modes that can only be set once and take args, then modes that can only be set once but take no args.
std::string listModes, // Lists like mode b
paramOnSetAndUnsetModes, // Properties like k
paramOnSetOnlyModes, // Properties like f
paramLessModes; // Properties like z
std::unordered_map<char, char> prefixMap; // Maps for example '@' to 'o'
bool isListMode(char mode) {
return listModes.find(mode) != std::string::npos;
}
bool takesNoParam(char mode) {
return paramLessModes.find(mode) != paramLessModes.npos;
}
bool takesParamOnUnset(char mode) {
return isListMode(mode) ||
paramOnSetAndUnsetModes.find(mode) != std::string::npos;
}
bool takesParamOnSet(char mode) {
return paramOnSetOnlyModes.find(mode) != std::string::npos ||
takesParamOnUnset(mode);
}
} channelModes;
async::result<void> wait_ready() {
if (!ready) {
co_await ready_event.wait();
}
}
void mark_ready() {
ready = true;
ready_event.raise();
}
};
class Instance {
std::reference_wrapper<uvpp::loop_service> s;
uvpp::Addr addr;
size_t lastUUID = 0;
bool server_bursting = true, client_bursting = true;
bool authed = false;
public:
std::unique_ptr<uvpp::tcp> socket;
const Config config;
Config::Server connected_server;
NetworkInfo netInfo;
Cache cache;
std::vector<std::unique_ptr<ServiceBase>> services;
Instance(uvpp::loop_service &s, const Config& config) : s(s), config(config) {
addr = uvpp::make_ipv4(config.connection.addr, config.connection.port);
}
async::result<void> run();
async::result<void> login();
async::result<void> burst();
async::result<void> process(const Command);
async::result<void> process(Event);
async::result<void> send_event(const Event&);
async::result<void> send_ping();
// utils.cpp
UUID UUIDGen();
};
#endif