mirror of
https://gitlab.com/niansa/asbots.git
synced 2025-03-06 20:48:25 +01:00
295 lines
9.2 KiB
C++
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
|