mirror of
https://gitlab.com/niansa/asbots.git
synced 2025-03-06 20:48:25 +01:00
Added configuration parser
This commit is contained in:
parent
085ff14ea6
commit
ee52874033
13 changed files with 150 additions and 42 deletions
|
@ -11,7 +11,7 @@ add_executable(asbots
|
|||
utility.cpp
|
||||
serviceBase.cpp
|
||||
services/test.cpp
|
||||
config.cpp
|
||||
configParser.cpp
|
||||
)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
@ -19,3 +19,5 @@ pkg_check_modules(libasync-uv REQUIRED IMPORTED_TARGET libasync-uv)
|
|||
pkg_check_modules(fmt REQUIRED IMPORTED_TARGET fmt)
|
||||
|
||||
target_link_libraries(asbots PRIVATE PkgConfig::libasync-uv PkgConfig::fmt)
|
||||
|
||||
configure_file(config.inil config.inil COPYONLY)
|
||||
|
|
19
config.cpp
19
config.cpp
|
@ -1,19 +0,0 @@
|
|||
#include "config.hpp"
|
||||
|
||||
|
||||
|
||||
Config config = {
|
||||
.connection = {
|
||||
.addr = "127.0.0.1",
|
||||
.port = 6667,
|
||||
},
|
||||
.auth = {
|
||||
.send_password = "849372523412",
|
||||
.accept_password = "843574835765"
|
||||
},
|
||||
.server = {
|
||||
.name = "services.",
|
||||
.description = "A test service!!",
|
||||
.uid = "23X"
|
||||
}
|
||||
};
|
14
config.hpp
14
config.hpp
|
@ -3,10 +3,17 @@
|
|||
#include "uid.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
|
||||
struct Config {
|
||||
struct ParseError : public std::runtime_error {
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
struct Connection {
|
||||
std::string addr;
|
||||
int port;
|
||||
|
@ -19,5 +26,10 @@ struct Config {
|
|||
SUID uid; // Fourth one reserved for null-terminator
|
||||
bool hidden = false;
|
||||
} server;
|
||||
} extern config;
|
||||
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, std::string>> misc;
|
||||
};
|
||||
|
||||
|
||||
Config parseConfig(const std::filesystem::path& path);
|
||||
#endif
|
||||
|
|
19
config.inil
Normal file
19
config.inil
Normal file
|
@ -0,0 +1,19 @@
|
|||
[connection]
|
||||
addr: 127.0.0.1
|
||||
port: 6667
|
||||
end
|
||||
|
||||
[auth]
|
||||
send_password: 849372523412
|
||||
accept_password: 843574835765
|
||||
end
|
||||
|
||||
[server]
|
||||
name: services.
|
||||
description: A test service!!
|
||||
uid: 23X
|
||||
end
|
||||
|
||||
[testservice]
|
||||
channel: #testservice
|
||||
end
|
75
configParser.cpp
Normal file
75
configParser.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
#include "config.hpp"
|
||||
#include "uid.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
|
||||
|
||||
inline void noop() {}
|
||||
|
||||
Config parseConfig(const std::filesystem::path& path) {
|
||||
Config fres;
|
||||
auto file = std::ifstream(path);
|
||||
// Read through all sections
|
||||
while (!file.eof()) {
|
||||
std::string head;
|
||||
std::string_view section;
|
||||
file >> head;
|
||||
// Skip some emptynes
|
||||
if (head.empty()) {
|
||||
continue;
|
||||
}
|
||||
// Check section head syntax
|
||||
if (head.size() < 3 || !head.starts_with('[') || !head.ends_with(']')) {
|
||||
throw Config::ParseError("Invalid head (section identifier) syntax");
|
||||
}
|
||||
// Extract actual section name
|
||||
section = {head.data()+1, head.size()-2};
|
||||
while (!file.eof()) {
|
||||
// Get key and value
|
||||
std::string line;
|
||||
std::getline(file, line);
|
||||
// Check for empty line
|
||||
if (line.empty()) {
|
||||
continue;
|
||||
}
|
||||
// Split into key and value
|
||||
auto [key, value] = Utility::splitOnce(line, ": ");
|
||||
// Check for "end"
|
||||
if (key == "end") {
|
||||
break;
|
||||
}
|
||||
// Do stuff depending on current section using macro magic
|
||||
# define SECTION(name, ...) if (section == #name) {auto &d = fres.name; __VA_ARGS__; continue;;} noop()
|
||||
# define ASSIGN(e_key, val) if (key == #e_key) {d.e_key = val; continue;} noop()
|
||||
SECTION(connection, {
|
||||
ASSIGN(addr, value);
|
||||
ASSIGN(port, std::stoi(std::string(value)));
|
||||
});
|
||||
SECTION(auth, {
|
||||
ASSIGN(send_password, value);
|
||||
ASSIGN(accept_password, value);
|
||||
});
|
||||
SECTION(server, {
|
||||
ASSIGN(name, value);
|
||||
ASSIGN(description, value);
|
||||
ASSIGN(uid, SUID(value));
|
||||
ASSIGN(hidden, value=="true");
|
||||
});
|
||||
// Misc keys
|
||||
auto s_res = fres.misc.find(std::string(section));
|
||||
if (s_res == fres.misc.end()) {
|
||||
fres.misc[std::string(section)] = {{std::string(key), std::string(value)}};
|
||||
} else {
|
||||
s_res->second[std::string(key)] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return resulting Config object
|
||||
return fres;
|
||||
}
|
16
instance.cpp
16
instance.cpp
|
@ -34,7 +34,7 @@ void Event::parse(std::string_view str) {
|
|||
name = std::move(split[1]);
|
||||
if (split.size() == 3) {
|
||||
// Get args and text
|
||||
std::tie(raw_args, text) = colonSplit(split[2]);
|
||||
std::tie(raw_args, text) = splitOnce(split[2], " :");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ void Command::parse(std::string_view str) {
|
|||
auto split = strSplit(str, ' ', 1);
|
||||
name = std::move(split[0]);
|
||||
// Get text from args
|
||||
std::tie(args, text) = colonSplit(split[1]);
|
||||
std::tie(args, text) = splitOnce(split[1], " :");
|
||||
}
|
||||
|
||||
|
||||
|
@ -248,9 +248,9 @@ async::result<void> Instance::run() {
|
|||
}
|
||||
|
||||
async::result<void> Instance::login() {
|
||||
co_await socket->send("PASS {} TS 6 :{}\n"_format(config.get().auth.send_password, config.get().server.uid.str()));
|
||||
co_await socket->send("PASS {} TS 6 :{}\n"_format(config.auth.send_password, config.server.uid.str()));
|
||||
co_await socket->send("CAPAB :QS EX IE KLN UNKLN ENCAP TB SERVICES EUID EOPMOD MLOCK\n"sv);
|
||||
co_await socket->send("SERVER {} 1 :{}{}\n"_format(config.get().server.name, config.get().server.hidden ? "(H) " : "", config.get().server.description));
|
||||
co_await socket->send("SERVER {} 1 :{}{}\n"_format(config.server.name, config.server.hidden ? "(H) " : "", config.server.description));
|
||||
co_await socket->send("SVINFO 6 3 0 :{}\n"_format(time(nullptr)));
|
||||
}
|
||||
|
||||
|
@ -268,7 +268,7 @@ async::result<void> Instance::process(const Command command) {
|
|||
if (command.name == "PASS") {
|
||||
// Check password
|
||||
auto given_password = strSplit(command.args, ' ', 1)[0];
|
||||
if (given_password != config.get().auth.accept_password) {
|
||||
if (given_password != config.auth.accept_password) {
|
||||
throw ConnectionError("Server supplied wrong password during authentication");
|
||||
}
|
||||
authed = true;
|
||||
|
@ -277,7 +277,7 @@ async::result<void> Instance::process(const Command command) {
|
|||
} else if (command.name == "ERROR") {
|
||||
throw ConnectionError(command.dump());
|
||||
} else if (command.name == "SQUIT") {
|
||||
if (command.args == config.get().server.uid.str()) {
|
||||
if (command.args == config.server.uid.str()) {
|
||||
throw ConnectionError(command.dump());
|
||||
}
|
||||
} else if (!authed) {
|
||||
|
@ -294,7 +294,7 @@ async::result<void> Instance::process(const Command command) {
|
|||
co_await burst();
|
||||
}
|
||||
// Reply
|
||||
co_await socket->send(":{} PONG {} {} :{}\n"_format(config.get().server.uid.str(), config.get().server.name, command.args, command.text));
|
||||
co_await socket->send(":{} PONG {} {} :{}\n"_format(config.server.uid.str(), config.server.name, command.args, command.text));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -307,7 +307,7 @@ async::result<void> Instance::process(Event event) {
|
|||
// Fetched info
|
||||
else if (event.name == "005") {
|
||||
// Check if that 005 was for me
|
||||
if (event.args[0] != config.get().server.uid.str()) {
|
||||
if (event.args[0] != config.server.uid.str()) {
|
||||
co_return;
|
||||
}
|
||||
// Split the list
|
||||
|
|
|
@ -227,7 +227,7 @@ class Instance {
|
|||
|
||||
public:
|
||||
std::unique_ptr<uvpp::tcp> socket;
|
||||
std::reference_wrapper<const Config> config;
|
||||
const Config config;
|
||||
Config::Server connected_server;
|
||||
NetworkInfo netInfo;
|
||||
Cache cache;
|
||||
|
|
2
main.cpp
2
main.cpp
|
@ -15,7 +15,7 @@ int main() {
|
|||
async::queue_scope qs{&rq};
|
||||
loop_service service;
|
||||
|
||||
Instance instance(service, config);
|
||||
Instance instance(service, parseConfig("config.inil"));
|
||||
instance.services.push_back(std::make_unique<TestService>());
|
||||
|
||||
async::detach(instance.run());
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#include "instance.hpp"
|
||||
|
||||
#include <async/result.hpp>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
|
@ -11,3 +13,19 @@ async::result<void> ServiceBase::mark_ready(const User& user) {
|
|||
co_await i->send_event(user.get_euid());
|
||||
ready = true;
|
||||
}
|
||||
|
||||
std::optional<std::string_view> ServiceBase::getConfig(const std::string& section, const std::string& key) {
|
||||
auto &cfg = i->config.misc;
|
||||
// Get section
|
||||
auto s_res = cfg.find(section);
|
||||
if (s_res == cfg.end()) {
|
||||
return {};
|
||||
}
|
||||
// Get key
|
||||
auto k_res = s_res->second.find(key);
|
||||
if (k_res == s_res->second.end()) {
|
||||
return {};
|
||||
}
|
||||
// Return final result
|
||||
return {k_res->second};
|
||||
}
|
||||
|
|
|
@ -8,6 +8,9 @@ class Instance;
|
|||
#include "incompletes.hpp"
|
||||
|
||||
#include <async/result.hpp>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <optional>
|
||||
|
||||
|
||||
|
||||
|
@ -22,5 +25,6 @@ public:
|
|||
virtual async::result<void> on_direct_privmsg(std::string_view msg, u_User& author) = 0;
|
||||
|
||||
async::result<void> mark_ready(const User& user);
|
||||
std::optional<std::string_view> getConfig(const std::string& section, const std::string& key);
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -8,13 +8,10 @@ using fmt::operator""_format;
|
|||
|
||||
|
||||
|
||||
const auto serviceChannelName = "#servicechannel";
|
||||
|
||||
|
||||
async::result<void> TestService::intitialize() {
|
||||
co_await i->netInfo.wait_ready();
|
||||
user = {
|
||||
.server = i->config.get().server.uid,
|
||||
.server = i->config.server.uid,
|
||||
.nick = "services",
|
||||
.ident = "services",
|
||||
.realhost = "services.",
|
||||
|
@ -23,13 +20,13 @@ async::result<void> TestService::intitialize() {
|
|||
.loginName = "serviceD"
|
||||
};
|
||||
Channel serviceChannel = {
|
||||
.server = i->config.get().server.uid,
|
||||
.name = serviceChannelName
|
||||
.server = i->config.server.uid,
|
||||
.name = std::string(getConfig("testservice", "channel").value_or("#services"))
|
||||
};
|
||||
co_await mark_ready(user);
|
||||
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_notice("Test... Hello world!", serviceChannelName));
|
||||
co_await i->send_event(user.get_notice("Test... Hello world!", serviceChannel.name));
|
||||
}
|
||||
|
||||
async::result<void> TestService::on_event(const Event& event) {
|
||||
|
|
|
@ -23,9 +23,9 @@ std::vector<std::string_view> strSplit(std::string_view s, char delimiter, size_
|
|||
return to_return;
|
||||
}
|
||||
|
||||
std::tuple<std::string_view, std::string_view> colonSplit(std::string_view s) {
|
||||
std::tuple<std::string_view, std::string_view> splitOnce(std::string_view s, std::string_view at) {
|
||||
// Find the colon
|
||||
auto colonPos = s.find(" :");
|
||||
auto colonPos = s.find(at);
|
||||
if (colonPos == s.npos) {
|
||||
return {s, ""};
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ std::string lowers(std::string_view str) {
|
|||
static const char UUIDChars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
UUID Instance::UUIDGen() {
|
||||
size_t numUUID = ++lastUUID;
|
||||
UUID fres = config.get().server.uid.str();
|
||||
UUID fres = config.server.uid.str();
|
||||
for (auto it = fres.array.end() - 1; it != fres.array.begin()+SUID_len-1; it--) {
|
||||
auto idx = std::min(numUUID, sizeof(UUIDChars)-1);
|
||||
*it = UUIDChars[idx];
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace Utility {
|
|||
constexpr size_t strSplitInf = 0;
|
||||
|
||||
std::vector<std::string_view> strSplit(std::string_view s, char delimiter, size_t times = strSplitInf);
|
||||
std::tuple<std::string_view, std::string_view> colonSplit(std::string_view s);
|
||||
std::tuple<std::string_view, std::string_view> splitOnce(std::string_view s, std::string_view at);
|
||||
void argsSizeCheck(std::string_view where, std::vector<std::string_view> args, size_t expected);
|
||||
std::string lowers(std::string_view str);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue