mirror of
https://gitlab.com/niansa/asbots.git
synced 2025-03-06 20:48:25 +01:00
First very very very basic ChanServ implementation
This commit is contained in:
parent
8773d0b886
commit
541239b2d5
11 changed files with 81 additions and 67 deletions
|
@ -33,6 +33,9 @@ struct DesyncError : public std::exception {
|
|||
const char *what() const throw() {
|
||||
return "Server has desynced!!!";
|
||||
}
|
||||
DesyncError() {
|
||||
abort();
|
||||
}
|
||||
};
|
||||
|
||||
struct InsufficientArgsError : public ParseError {
|
||||
|
|
|
@ -25,6 +25,4 @@ struct User;
|
|||
struct Channel;
|
||||
struct Cache;
|
||||
struct NetworkInfo;
|
||||
using u_User = std::unique_ptr<User>;
|
||||
using u_Channel = std::unique_ptr<Channel>;
|
||||
#endif
|
||||
|
|
43
instance.cpp
43
instance.cpp
|
@ -145,8 +145,8 @@ void User::parse_euid(const Event& event, NetworkInfo& netInfo) {
|
|||
realname = std::move(event.text);
|
||||
}
|
||||
|
||||
void User::removeChannel(const u_Channel& channel) {
|
||||
std::remove_if(channels.begin(), channels.end(), [&] (auto obj) {return obj == &channel;});
|
||||
void User::removeChannel(const Channel *channel) {
|
||||
std::remove_if(channels.begin(), channels.end(), [&] (auto obj) {return obj == channel;});
|
||||
}
|
||||
|
||||
|
||||
|
@ -154,13 +154,14 @@ void Channel::parse_sjoin(const Event& event, Cache& cache, NetworkInfo& netInfo
|
|||
this->server = std::get<SUID>(event.sender.id);
|
||||
// Check size
|
||||
argsSizeCheck("SJOIN", event.args, 3);
|
||||
// Move values
|
||||
name = std::move(event.args[1]);
|
||||
// Set values
|
||||
creationTime = std::stoull(std::string(event.args[0]));
|
||||
name = event.args[1];
|
||||
mode.parse<true>(event.args[2], netInfo);
|
||||
// Get members
|
||||
for (auto& raw_uuid : strSplit(event.text, ' ')) {
|
||||
// Erase prefix
|
||||
if (raw_uuid.size() > UUID_len) {
|
||||
while (raw_uuid.size() > UUID_len) {
|
||||
char prefixChar = raw_uuid[0];
|
||||
raw_uuid = {raw_uuid.data()+1, raw_uuid.size()-1};
|
||||
char modeChar = netInfo.channelModes.prefixMap[prefixChar];
|
||||
|
@ -172,18 +173,18 @@ void Channel::parse_sjoin(const Event& event, Cache& cache, NetworkInfo& netInfo
|
|||
if (res == cache.users.end()) {
|
||||
throw DesyncError();
|
||||
}
|
||||
u_User& member = *res;
|
||||
User *member = res->get();
|
||||
// Append member to list
|
||||
members.push_back(&member);
|
||||
members.push_back(member);
|
||||
}
|
||||
}
|
||||
|
||||
void Channel::removeMember(const u_User& member) {
|
||||
std::remove_if(members.begin(), members.end(), [&] (auto obj) {return *obj == member;});
|
||||
void Channel::removeMember(const User *member) {
|
||||
std::remove_if(members.begin(), members.end(), [&] (auto obj) {return obj == member;});
|
||||
}
|
||||
|
||||
|
||||
std::vector<u_User>::iterator Cache::find_user_by_nick(std::string_view nick) {
|
||||
std::vector<std::unique_ptr<User>>::iterator Cache::find_user_by_nick(std::string_view nick) {
|
||||
for (auto it = users.begin(); ; it++) {
|
||||
if (it == users.end() || it->get()->nick == nick) {
|
||||
return it;
|
||||
|
@ -191,7 +192,7 @@ std::vector<u_User>::iterator Cache::find_user_by_nick(std::string_view nick) {
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<u_User>::iterator Cache::find_user_by_uid(const UUID& uid) {
|
||||
std::vector<std::unique_ptr<User>>::iterator Cache::find_user_by_uid(const UUID& uid) {
|
||||
for (auto it = users.begin(); ; it++) {
|
||||
if (it == users.end() || it->get()->uid == uid) {
|
||||
return it;
|
||||
|
@ -199,7 +200,7 @@ std::vector<u_User>::iterator Cache::find_user_by_uid(const UUID& uid) {
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<u_Channel>::iterator Cache::find_channel_by_name(std::string_view name) {
|
||||
std::vector<std::unique_ptr<Channel>>::iterator Cache::find_channel_by_name(std::string_view name) {
|
||||
for (auto it = channels.begin(); ; it++) {
|
||||
if (it == channels.end() || it->get()->name == name) {
|
||||
return it;
|
||||
|
@ -388,7 +389,7 @@ async::result<void> Instance::process(Event event) {
|
|||
}
|
||||
// Delete user from all channels
|
||||
for (auto& channel : res->get()->channels) {
|
||||
channel->get()->removeMember({*res});
|
||||
channel->removeMember(res->get());
|
||||
}
|
||||
// Delete user from cache
|
||||
cache.users.erase(res);
|
||||
|
@ -466,10 +467,10 @@ async::result<void> Instance::process(Event event) {
|
|||
throw DesyncError();
|
||||
}
|
||||
// Assign user to channel and vice versa
|
||||
c_res->get()->members.push_back(&*u_res);
|
||||
u_res->get()->channels.push_back(&*c_res);
|
||||
c_res->get()->members.push_back(u_res->get());
|
||||
u_res->get()->channels.push_back(c_res->get());
|
||||
// Update channel modes
|
||||
c_res->get()->mode.parse<true>(event.args[2], netInfo);
|
||||
c_res->get()->mode.parse<true>(event.fromArgToEnd(event.args[2]), netInfo);
|
||||
} else if (event.name == "PART" || event.name == "KICK") {
|
||||
// User left channel
|
||||
// Get channel from cache
|
||||
|
@ -483,8 +484,8 @@ async::result<void> Instance::process(Event event) {
|
|||
throw DesyncError();
|
||||
}
|
||||
// Remove channel from both user and channel
|
||||
c_res->get()->removeMember(*u_res);
|
||||
u_res->get()->removeChannel(*c_res);
|
||||
c_res->get()->removeMember(u_res->get());
|
||||
u_res->get()->removeChannel(c_res->get());
|
||||
} else if (event.name == "TMODE" || event.name == "BMASK") {
|
||||
// Channel modes changed
|
||||
argsSizeCheck(event.name, event.args, 3);
|
||||
|
@ -492,7 +493,7 @@ async::result<void> Instance::process(Event event) {
|
|||
std::string_view modes, channelName;
|
||||
{
|
||||
channelName = event.args[1];
|
||||
modes = event.args[2];
|
||||
modes = event.fromArgToEnd(event.args[2]);
|
||||
}
|
||||
// Get channel from cache
|
||||
auto c_res = cache.find_channel_by_name(channelName);
|
||||
|
@ -500,7 +501,7 @@ async::result<void> Instance::process(Event event) {
|
|||
throw DesyncError();
|
||||
}
|
||||
// Apply changes
|
||||
c_res->get()->mode.parse<true>("+{} {}"_format(modes, event.text), netInfo);
|
||||
c_res->get()->mode.parse<true>("+{}"_format(modes), netInfo);
|
||||
}
|
||||
// Messages
|
||||
else if (event.name == "PRIVMSG") {
|
||||
|
@ -517,7 +518,7 @@ async::result<void> Instance::process(Event event) {
|
|||
// 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);
|
||||
co_await service->on_direct_privmsg(event.text, res->get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
34
instance.hpp
34
instance.hpp
|
@ -52,6 +52,9 @@ struct Event { // DON'T std::move() this class. EVER!!!
|
|||
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 {
|
||||
|
@ -83,17 +86,18 @@ struct Channel {
|
|||
SUID server;
|
||||
std::string name;
|
||||
ModeSet mode;
|
||||
time_t timeStamp = 0;
|
||||
std::string topic;
|
||||
std::vector<u_User*> members;
|
||||
std::vector<User*> members;
|
||||
|
||||
void parse_sjoin(const Event &event, Cache& cache, NetworkInfo& netInfo);
|
||||
void removeMember(const u_User& member);
|
||||
void removeMember(const User *member);
|
||||
|
||||
Event get_sjoin(const UUID& initial_user) const {
|
||||
Event get_sjoin(const UUID& initial_user, const SUID& my_sid) const {
|
||||
return {
|
||||
.sender = SUID(server),
|
||||
.sender = my_sid,
|
||||
.name = "SJOIN",
|
||||
.raw_args = fmt::format("{} {} +{}", time(nullptr), name, mode.str),
|
||||
.raw_args = fmt::format("{} {} +", timeStamp?timeStamp:time(nullptr), name),
|
||||
.text = fmt::format("@{}", initial_user.str())
|
||||
};
|
||||
}
|
||||
|
@ -105,11 +109,11 @@ struct Channel {
|
|||
.text = std::string(topic)
|
||||
};
|
||||
}
|
||||
Event get_tmode(const ModeSet& modes, const UUID& user) const {
|
||||
Event get_tmode(const ModeSet& modes, const AnyUID& sender) const {
|
||||
return {
|
||||
.sender = user,
|
||||
.sender = AnyUID(sender),
|
||||
.name = "TMODE",
|
||||
.raw_args = fmt::format("{} {} {}", time(nullptr), name, modes.fullStr())
|
||||
.raw_args = fmt::format("{} {} +{}", timeStamp?timeStamp:time(nullptr), name, modes.fullStr())
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -125,11 +129,11 @@ struct User {
|
|||
std::string realhost;
|
||||
UUID uid;
|
||||
std::string realname;
|
||||
std::vector<u_Channel*> channels;
|
||||
std::vector<Channel*> channels;
|
||||
std::optional<std::string> loginName;
|
||||
|
||||
void parse_euid(const Event &event, NetworkInfo& netInfo);
|
||||
void removeChannel(const u_Channel &channel);
|
||||
void removeChannel(const Channel *channel);
|
||||
|
||||
Event get_euid() const {
|
||||
return {
|
||||
|
@ -200,12 +204,12 @@ struct User {
|
|||
};
|
||||
|
||||
struct Cache {
|
||||
std::vector<u_User> users;
|
||||
std::vector<u_Channel> channels;
|
||||
std::vector<std::unique_ptr<User>> users;
|
||||
std::vector<std::unique_ptr<Channel>> channels;
|
||||
|
||||
std::vector<u_User>::iterator find_user_by_nick(std::string_view nick);
|
||||
std::vector<u_User>::iterator find_user_by_uid(const UUID& uid);
|
||||
std::vector<u_Channel>::iterator find_channel_by_name(std::string_view name);
|
||||
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 {
|
||||
|
|
2
main.cpp
2
main.cpp
|
@ -19,6 +19,7 @@
|
|||
#include "instance.hpp"
|
||||
|
||||
#include "services/nickserv.hpp"
|
||||
#include "services/chanserv.hpp"
|
||||
|
||||
#include <uvpp.hpp>
|
||||
#include <async/result.hpp>
|
||||
|
@ -34,6 +35,7 @@ int main() {
|
|||
|
||||
Instance instance(service, parseConfig("config.inil"));
|
||||
instance.services.push_back(std::make_unique<NickServ>());
|
||||
instance.services.push_back(std::make_unique<ChanServ>());
|
||||
|
||||
async::detach(instance.run());
|
||||
async::run_forever(rq.run_token(), loop_service_wrapper{service});
|
||||
|
|
|
@ -50,7 +50,7 @@ std::optional<std::string_view> ServiceBase::getConfig(const std::string& sectio
|
|||
return {k_res->second};
|
||||
}
|
||||
|
||||
async::result<void> ServiceBase::on_direct_privmsg(std::string_view msg, u_User& author) {
|
||||
async::result<void> ServiceBase::on_direct_privmsg(std::string_view msg, User *author) {
|
||||
auto split = Utility::strSplit(msg, ' ');
|
||||
User user = {.server = i->config.server.uid, .uid = uuid};
|
||||
|
||||
|
|
|
@ -38,11 +38,11 @@ public:
|
|||
UUID uuid;
|
||||
Instance *i;
|
||||
bool ready = false;
|
||||
std::unordered_map<std::string_view, std::tuple<std::function<async::result<void> (u_User& sender, const std::vector<std::string_view>& args)>, std::string_view, size_t, bool>> commands;
|
||||
std::unordered_map<std::string_view, std::tuple<std::function<async::result<void> (User *sender, const std::vector<std::string_view>& args)>, std::string_view, size_t, bool>> commands;
|
||||
|
||||
virtual async::result<void> intitialize() = 0;
|
||||
virtual async::result<void> on_event(const Event& event);
|
||||
virtual async::result<void> on_direct_privmsg(std::string_view msg, u_User& author);
|
||||
virtual async::result<void> on_direct_privmsg(std::string_view msg, User *author);
|
||||
|
||||
async::result<void> mark_ready(const User& user);
|
||||
std::optional<std::string_view> getConfig(const std::string& section, const std::string& key);
|
||||
|
|
|
@ -38,7 +38,8 @@ async::result<void> ChanServ::intitialize() {
|
|||
.uid = uuid,
|
||||
.realname = i->netInfo.name
|
||||
};
|
||||
co_await mark_ready(user);
|
||||
|
||||
opMe.parse<true>("+o {}"_format(user.uid.str()), i->netInfo);
|
||||
|
||||
using namespace sqlite_orm;
|
||||
storage = std::make_unique<Storage>(make_storage(
|
||||
|
@ -51,16 +52,16 @@ async::result<void> ChanServ::intitialize() {
|
|||
storage->sync_schema();
|
||||
|
||||
commands = {
|
||||
{"register", {[this] (u_User& sender, std::vector<std::string_view> args) -> async::result<void> {
|
||||
{"register", {[this] (User *sender, std::vector<std::string_view> args) -> async::result<void> {
|
||||
// Get channel
|
||||
u_Channel *channel;
|
||||
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;
|
||||
channel = res->get();
|
||||
}
|
||||
// Check if user is operator in the channel
|
||||
// TODO
|
||||
|
@ -68,7 +69,7 @@ async::result<void> ChanServ::intitialize() {
|
|||
{
|
||||
auto channelregs_found = storage->get_all<ChannelReg>(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_await i->send_event(user.get_notice("Error: {} has already been registered!"_format(args[0]), sender->uid));
|
||||
co_return;
|
||||
}
|
||||
}
|
||||
|
@ -85,17 +86,21 @@ async::result<void> ChanServ::intitialize() {
|
|||
}, "<channel>", 1, true}
|
||||
},
|
||||
};
|
||||
|
||||
co_await mark_ready(user);
|
||||
}
|
||||
|
||||
async::result<void> 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<u_User*> 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);
|
||||
async::result<void> ChanServ::initializeChannel(Channel *channel, const ChannelReg& channelReg) {
|
||||
// Get all owners nicks mode +o
|
||||
ModeSet initialModes;
|
||||
for (const auto& member : channel->members) {
|
||||
if (member->loginName.has_value() && member->loginName.value() == channelReg.owner) {
|
||||
initialModes.parse<true>("+o {}"_format(member->uid.str()), i->netInfo);
|
||||
}
|
||||
}
|
||||
for (const auto member : to_mark_as_admin) {
|
||||
co_await i->send_event(channel->get()->get_tmode({"o", {std::tuple<char, std::string>{'o', member->get()->uid.str()}}}, member->get()->uid));
|
||||
}
|
||||
// Join the channel
|
||||
auto je = channel->get_sjoin(user.uid, i->config.server.uid); // Needs to be done seperately to avoid some... super weird... crash
|
||||
co_await i->send_event(je);
|
||||
// Grant owners nicks OP
|
||||
co_await i->send_event(channel->get_tmode(initialModes, user.uid));
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
* 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 _TEST_HPP
|
||||
#define _TEST_HPP
|
||||
#ifndef _CHANSERV_HPP
|
||||
#define _CHANSERV_HPP
|
||||
#include "../instance.hpp"
|
||||
#include "../serviceBase.hpp"
|
||||
|
||||
|
@ -32,14 +32,15 @@ struct ChannelReg {
|
|||
std::string name, owner;
|
||||
};
|
||||
|
||||
class ChanServ : public ServiceBase {
|
||||
using Storage = sqlite_orm::internal::storage_t<sqlite_orm::internal::table_t<ChannelReg, sqlite_orm::internal::column_t<ChannelReg, int, const int& (ChannelReg::*)() const, void (ChannelReg::*)(int), sqlite_orm::constraints::autoincrement_t, sqlite_orm::constraints::primary_key_t<> >, sqlite_orm::internal::column_t<ChannelReg, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >& (ChannelReg::*)() const, void (ChannelReg::*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>, sqlite_orm::internal::column_t<ChannelReg, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >& (ChannelReg::*)() const, void (ChannelReg::*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> > >;
|
||||
|
||||
class ChanServ : public ServiceBase {
|
||||
std::unique_ptr<Storage> storage;
|
||||
User user;
|
||||
ModeSet opMe;
|
||||
|
||||
virtual async::result<void> intitialize() override;
|
||||
|
||||
async::result<void> initializeChannel(u_Channel *channel, const ChannelReg& channelReg);
|
||||
async::result<void> initializeChannel(Channel *channel, const ChannelReg& channelReg);
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -55,7 +55,7 @@ async::result<void> NickServ::intitialize() {
|
|||
storage->sync_schema();
|
||||
|
||||
commands = {
|
||||
{"identify", {[this] (u_User& sender, const std::vector<std::string_view>& args) -> async::result<void> {
|
||||
{"identify", {[this] (User *sender, const std::vector<std::string_view>& args) -> async::result<void> {
|
||||
// Check that user is not already identified
|
||||
if (sender->loginName.has_value()) {
|
||||
co_await i->send_event(user.get_notice("Error: You've already been identified!", sender->uid));
|
||||
|
@ -88,12 +88,12 @@ async::result<void> NickServ::intitialize() {
|
|||
co_await i->send_event(user.get_notice("You have been identified!", sender->uid));
|
||||
}, "[username] <password>", 1, false}
|
||||
},
|
||||
{"deidentify", {[this] (u_User& sender, const std::vector<std::string_view>& args) -> async::result<void> {
|
||||
{"deidentify", {[this] (User *sender, const std::vector<std::string_view>& args) -> async::result<void> {
|
||||
co_await i->send_event(sender->get_encap_su("", i->config.server.uid));
|
||||
co_await i->send_event(user.get_notice("You have been deidentified!", sender->uid));
|
||||
}, "", 0, true}
|
||||
},
|
||||
{"register", {[this] (u_User& sender, std::vector<std::string_view> args) -> async::result<void> {
|
||||
{"register", {[this] (User *sender, std::vector<std::string_view> args) -> async::result<void> {
|
||||
// Check args
|
||||
if (args.size() != 2) {
|
||||
co_await i->send_event(user.get_notice("Error: Passwords may not contain spaces!", sender->uid));
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
* 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 _TEST_HPP
|
||||
#define _TEST_HPP
|
||||
#ifndef _NICKSERV_HPP
|
||||
#define _NICKSERV_HPP
|
||||
#include "../instance.hpp"
|
||||
#include "../serviceBase.hpp"
|
||||
|
||||
|
@ -32,9 +32,9 @@ struct Account {
|
|||
std::string name, email, password_hash;
|
||||
};
|
||||
|
||||
class NickServ : public ServiceBase {
|
||||
using Storage = sqlite_orm::internal::storage_t<sqlite_orm::internal::table_t<Account, sqlite_orm::internal::column_t<Account, int, const int& (Account::*)() const, void (Account::*)(int), sqlite_orm::constraints::autoincrement_t, sqlite_orm::constraints::primary_key_t<> >, sqlite_orm::internal::column_t<Account, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >& (Account::*)() const, void (Account::*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>, sqlite_orm::internal::column_t<Account, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >& (Account::*)() const, void (Account::*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>, sqlite_orm::internal::column_t<Account, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >& (Account::*)() const, void (Account::*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> > >;
|
||||
|
||||
class NickServ : public ServiceBase {
|
||||
std::unique_ptr<Storage> storage;
|
||||
User user;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue