diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d3c0f5..2a4207a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ add_executable(asbots utility.cpp serviceBase.cpp services/nickserv.cpp + services/chanserv.cpp configParser.cpp ) diff --git a/instance.hpp b/instance.hpp index 3dd07ea..1439e99 100644 --- a/instance.hpp +++ b/instance.hpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +67,14 @@ struct ModeSet { std::string str; std::vector> params; + std::string fullStr() const { + std::ostringstream o; + o << str; + for (const auto& [mode, param] : params) { + o << ' ' << param; + } + return o.str(); + } template void parse(std::string_view str, NetworkInfo& netInfo); }; @@ -96,6 +105,13 @@ struct Channel { .text = std::string(topic) }; } + Event get_tmode(const ModeSet& modes, const UUID& user) const { + return { + .sender = user, + .name = "TMODE", + .raw_args = fmt::format("{} {} {}", time(nullptr), name, modes.fullStr()) + }; + } }; struct User { diff --git a/services/chanserv.cpp b/services/chanserv.cpp new file mode 100644 index 0000000..98f883d --- /dev/null +++ b/services/chanserv.cpp @@ -0,0 +1,101 @@ +/* + * 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 . + */ +#include "chanserv.hpp" +#include "../instance.hpp" + +#include +#include +#include +#include +#include +#include + +using fmt::operator""_format; + + + +async::result ChanServ::intitialize() { + co_await i->netInfo.wait_ready(); + user = { + .server = i->config.server.uid, + .nick = std::string(getConfig("ChanServ", "nickname").value_or("ChanServ")), + .realhost = i->config.server.name, + .uid = uuid, + .realname = i->netInfo.name + }; + co_await mark_ready(user); + + using namespace sqlite_orm; + storage = std::make_unique(make_storage( + std::string(getConfig("ChanServ", "database").value_or("chanserv.sqlite")), + make_table("users", + make_column("id", &ChannelReg::id, autoincrement(), primary_key()), + make_column("name", &ChannelReg::name), + make_column("owner", &ChannelReg::owner)) + )); + storage->sync_schema(); + + commands = { + {"register", {[this] (u_User& sender, std::vector args) -> async::result { + // Get channel + u_Channel *channel; + { + auto res = i->cache.find_channel_by_name(args[0]); + if (res == i->cache.channels.end()) { + co_await i->send_event(user.get_notice("Error: {} does not exist!"_format(args[0]), sender->uid)); + co_return; + } + channel = &*res; + } + // Check if user is operator in the channel + // TODO + // Check that account does not exist already + { + auto channelregs_found = storage->get_all(where(c(&ChannelReg::name) == std::string(args[0]))); + if (!channelregs_found.empty()) { + co_await i->send_event(user.get_notice("Error: {} has already been registered!"_format(sender->nick), sender->uid)); + co_return; + } + } + // Append it to database + ChannelReg reg = { + .name = std::string(args[0]), + .owner = sender->nick + }; + storage->insert(reg); + // Report success + co_await i->send_event(user.get_notice("{} has been registered!"_format(args[0]), sender->uid)); + // Initialize channel + co_await initializeChannel(channel, reg); + }, "", 1, true} + }, + }; +} + +async::result ChanServ::initializeChannel(u_Channel *channel, const ChannelReg& channelReg) { + // We need to loop twice because co_await'ing inside a loop through a non-local dynamically-sized container is DANGEROUS + std::vector to_mark_as_admin; + for (const auto member : channel->get()->members) { + if (member->get()->loginName.value_or("") == channelReg.owner) { + to_mark_as_admin.push_back(member); + } + } + for (const auto member : to_mark_as_admin) { + co_await i->send_event(channel->get()->get_tmode({"o", {std::tuple{'o', member->get()->uid.str()}}}, member->get()->uid)); + } +} diff --git a/services/chanserv.hpp b/services/chanserv.hpp new file mode 100644 index 0000000..1ea73df --- /dev/null +++ b/services/chanserv.hpp @@ -0,0 +1,45 @@ +/* + * 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 . + */ +#ifndef _TEST_HPP +#define _TEST_HPP +#include "../instance.hpp" +#include "../serviceBase.hpp" + +#include +#include +#include +#include "sqlite_orm.h" + + + +struct ChannelReg { + int id = -1; + std::string name, owner; +}; + +using Storage = sqlite_orm::internal::storage_t >, sqlite_orm::internal::column_t, std::allocator >, const std::__cxx11::basic_string, std::allocator >& (ChannelReg::*)() const, void (ChannelReg::*)(std::__cxx11::basic_string, std::allocator >)>, sqlite_orm::internal::column_t, std::allocator >, const std::__cxx11::basic_string, std::allocator >& (ChannelReg::*)() const, void (ChannelReg::*)(std::__cxx11::basic_string, std::allocator >)> > >; + +class ChanServ : public ServiceBase { + std::unique_ptr storage; + User user; + + virtual async::result intitialize() override; + + async::result initializeChannel(u_Channel *channel, const ChannelReg& channelReg); +}; +#endif diff --git a/services/nickserv.cpp b/services/nickserv.cpp index 3edc009..256704e 100644 --- a/services/nickserv.cpp +++ b/services/nickserv.cpp @@ -19,6 +19,7 @@ #include "../instance.hpp" #include +#include #include #include #include @@ -27,6 +28,8 @@ #include #include +using fmt::operator""_format; + async::result NickServ::intitialize() { @@ -70,7 +73,7 @@ async::result NickServ::intitialize() { // Get account from storage auto accounts_found = storage->get_all(where(c(&Account::name) == std::string(accountName))); if (accounts_found.empty()) { - co_await i->send_event(user.get_notice("Error: '"+std::string(accountName)+"' is not registered!", sender->uid)); + co_await i->send_event(user.get_notice("Error: {} is not registered!"_format(accountName), sender->uid)); co_return; } auto& account = accounts_found[0]; @@ -96,11 +99,11 @@ async::result NickServ::intitialize() { co_await i->send_event(user.get_notice("Error: Passwords may not contain spaces!", sender->uid)); co_return; } - // Check that user does not exist already + // Check that account does not exist already { auto accounts_found = storage->get_all(where(c(&Account::name) == sender->nick)); if (!accounts_found.empty()) { - co_await i->send_event(user.get_notice("Error: This nick has already been registered!", sender->uid)); + co_await i->send_event(user.get_notice("Error: {} has already been registered!"_format(sender->nick), sender->uid)); co_return; } } @@ -111,7 +114,7 @@ async::result NickServ::intitialize() { .password_hash = pwdHash(args[0]) }); // Report success - co_await i->send_event(user.get_notice("Your nickname has been registered!", sender->uid)); + co_await i->send_event(user.get_notice("{} has been registered!"_format(sender->nick), sender->uid)); }, " ", 2, false} }, }; diff --git a/services/nickserv.hpp b/services/nickserv.hpp index cdd5028..bbce1c5 100644 --- a/services/nickserv.hpp +++ b/services/nickserv.hpp @@ -20,11 +20,10 @@ #include "../instance.hpp" #include "../serviceBase.hpp" -#include "sqlite_orm.h" - #include #include #include +#include "sqlite_orm.h" @@ -37,7 +36,6 @@ using Storage = sqlite_orm::internal::storage_t storage; - User user; virtual async::result intitialize() override;