From ea47dbad64908941e596f733427d7cd52b571e61 Mon Sep 17 00:00:00 2001 From: Andrew Attali Date: Thu, 12 Sep 2024 14:46:29 +0200 Subject: [PATCH 1/2] feat: add bot registration on Server via BOT cmd --- include/Client.hpp | 3 ++ include/Commands/BotCommand.hpp | 15 +++++++++ include/Commands/KickCommand.hpp | 2 +- include/Commands/ModeCommand.hpp | 2 ++ include/Replies.hpp | 3 ++ include/Server.hpp | 2 ++ make/sources.mk | 1 + src/Channel.cpp | 4 +-- src/Client.cpp | 6 ++++ src/Command.cpp | 3 ++ src/Commands/BotCommand.cpp | 57 ++++++++++++++++++++++++++++++++ src/Commands/JoinCommand.cpp | 3 +- src/Commands/KickCommand.cpp | 32 ++++++++---------- src/Commands/ModeCommand.cpp | 9 +++++ src/Replies.cpp | 23 ++++++++++++- src/Server.cpp | 19 ++++++++--- src/main.cpp | 1 - 17 files changed, 157 insertions(+), 28 deletions(-) create mode 100644 include/Commands/BotCommand.hpp create mode 100644 src/Commands/BotCommand.cpp diff --git a/include/Client.hpp b/include/Client.hpp index 1e5ee5f..50d070f 100644 --- a/include/Client.hpp +++ b/include/Client.hpp @@ -29,6 +29,7 @@ class Client { bool _invisible; bool _away; bool _awayNotify; + bool _bot; std::string _awayMsg; public: @@ -49,12 +50,14 @@ class Client { bool isAway(); bool isAwayNotify(); bool isInvisible(); + bool isBot(); bool hasCapEndedEarly(); State getState() const; void setState(State newState); void setInvisible(bool state); void setAway(bool state, std::string message); void setAwayNotify(bool state); + void setBot(bool state); void setCapEndedEarly(); void setNickname(std::string& nick); void setUsername(std::string& username); diff --git a/include/Commands/BotCommand.hpp b/include/Commands/BotCommand.hpp new file mode 100644 index 0000000..f00b624 --- /dev/null +++ b/include/Commands/BotCommand.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "Command.hpp" + +class BotCommand : public Command { + private: + std::string _key; + + public: + BotCommand(std::string source, std::vector params, + Client* client); + ~BotCommand(); + + void run(); +}; \ No newline at end of file diff --git a/include/Commands/KickCommand.hpp b/include/Commands/KickCommand.hpp index 644ae8c..2e606ba 100644 --- a/include/Commands/KickCommand.hpp +++ b/include/Commands/KickCommand.hpp @@ -7,7 +7,7 @@ class KickCommand : public Command { private: Channel* _channel; std::string _comment; - std::string _targetNickname; + Client* _target; void checkParams(Client* client, std::vector params); public: diff --git a/include/Commands/ModeCommand.hpp b/include/Commands/ModeCommand.hpp index 303601e..aa778e4 100644 --- a/include/Commands/ModeCommand.hpp +++ b/include/Commands/ModeCommand.hpp @@ -18,6 +18,7 @@ class ModeCommand : public Command { static modeMap initMap() { modeMap m; m['b'] = &ModeCommand::banMode; + m['B'] = &ModeCommand::botMode; m['l'] = &ModeCommand::limitMode; m['i'] = &ModeCommand::iModeDispatcher; m['k'] = &ModeCommand::keyMode; @@ -34,6 +35,7 @@ class ModeCommand : public Command { void executeMode(); void invisibleMode(bool oper, size_t& p); void banMode(bool oper, size_t& p); + void botMode(bool oper, size_t& p); void limitMode(bool oper, size_t& p); void inviteMode(bool oper, size_t& p); void keyMode(bool oper, size_t& p); diff --git a/include/Replies.hpp b/include/Replies.hpp index 6e83aa8..84c3b23 100644 --- a/include/Replies.hpp +++ b/include/Replies.hpp @@ -156,4 +156,7 @@ class Replies : public Message { static std::string RPL_SASLMECHS(); static std::string ERR_REGFAILED(); static std::string ERR_QUIT(); + static std::string ERR_INVALIDBOTKEY(Client* client); + static std::string RPL_YOUREPRIVBOT(Client* client); + static std::string RPL_NEWCHAN(Client* client, std::string chanName); }; diff --git a/include/Server.hpp b/include/Server.hpp index fc75535..63ab66b 100644 --- a/include/Server.hpp +++ b/include/Server.hpp @@ -66,6 +66,7 @@ class Server { std::string getChannelModes() const; std::string getRplSupport1() const; std::string getRplSupport2() const; + std::string getBotKey() const; size_t getMaxClients() const; std::set getClientsSet(std::map channels, Client* sender); @@ -76,6 +77,7 @@ class Server { Client* sender); void sendMessageIfAway(std::map channels, std::string message, Client* sender); + void notifyPrivBot(std::string chanName); static Server& getInstance(); diff --git a/make/sources.mk b/make/sources.mk index c729961..b7dabc4 100644 --- a/make/sources.mk +++ b/make/sources.mk @@ -10,6 +10,7 @@ SOURCES += ./src/Server.cpp SOURCES += ./src/ServerSocket.cpp SOURCES += ./src/Socket.cpp SOURCES += ./src/Commands/AwayCommand.cpp +SOURCES += ./src/Commands/BotCommand.cpp SOURCES += ./src/Commands/CapCommand.cpp SOURCES += ./src/Commands/InviteCommand.cpp SOURCES += ./src/Commands/JoinCommand.cpp diff --git a/src/Channel.cpp b/src/Channel.cpp index 63de1da..0d79a23 100644 --- a/src/Channel.cpp +++ b/src/Channel.cpp @@ -159,11 +159,11 @@ bool Channel::isInChannel(Client* client) const { } bool Channel::isInvited(Client* client) const { - return isOnList(_inviteList, client); + return isOnList(_inviteList, client) || client->isServerOperator(); } bool Channel::isBanned(Client* client) const { - return isOnList(_banList, client); + return isOnList(_banList, client) && !client->isServerOperator(); } bool Channel::isOperator(Client* client) const { diff --git a/src/Client.cpp b/src/Client.cpp index a637e5f..0477546 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -53,6 +53,8 @@ bool Client::isAwayNotify() { return this->_awayNotify; } bool Client::isInvisible() { return this->_invisible; } +bool Client::isBot() { return this->_bot; } + void Client::setCapEndedEarly() { this->_capEndedEarly = true; } void Client::setAway(bool state, string message) { @@ -64,6 +66,8 @@ void Client::setAwayNotify(bool state) { this->_awayNotify = state; } void Client::setInvisible(bool state) { this->_invisible = state; } +void Client::setBot(bool state) { this->_bot = state; } + void Client::setNickname(string& nick) { this->_nickname = nick; } void Client::setUsername(string& username) { this->_username = username; } @@ -81,6 +85,8 @@ string Client::getModes() { modes += "o"; if (isInvisible()) modes += "i"; + if (isBot()) + modes += "B"; return modes; } diff --git a/src/Command.cpp b/src/Command.cpp index cafde29..1a87d06 100644 --- a/src/Command.cpp +++ b/src/Command.cpp @@ -1,6 +1,7 @@ #include #include "AwayCommand.hpp" +#include "BotCommand.hpp" #include "CapCommand.hpp" #include "Command.hpp" #include "InviteCommand.hpp" @@ -32,6 +33,8 @@ Command* Command::create(string& data, Client* client) { if (command == "AWAY") return new AwayCommand(source, params, client); + else if (command == "BOT") + return new BotCommand(source, params, client); else if (command == "CAP") return new CapCommand(source, params, client); else if (command == "INVITE") diff --git a/src/Commands/BotCommand.cpp b/src/Commands/BotCommand.cpp new file mode 100644 index 0000000..7460b78 --- /dev/null +++ b/src/Commands/BotCommand.cpp @@ -0,0 +1,57 @@ +#include "BotCommand.hpp" +#include "Client.hpp" +#include "Exception.hpp" +#include "JoinCommand.hpp" +#include + +using std::map; +using std::string; +using std::vector; + +BotCommand::BotCommand(string source, vector params, Client* client) { + if (!client->isRegistered()) { + client->sendMessage(Replies::ERR_NOTREGISTERED()); + throw ClientException(); + } + + if (params.empty()) { + client->sendMessage(Replies::ERR_NEEDMOREPARAMS(client, "BOT")); + throw ClientException(); + } + + this->_source = source; + this->_params = params; + this->_client = client; + this->_key = params[0]; +} + +BotCommand::~BotCommand() {} + +void BotCommand::run() { + if (Server::getInstance().getBotKey() != _key) { + _client->sendMessage(Replies::ERR_INVALIDBOTKEY(_client)); + return; + } + + _client->setState(OPERATOR); + _client->setBot(true); + _client->sendMessage(Replies::RPL_YOUREOPER(_client)); + string reply = ":" + Server::getInstance().getSource() + " MODE " + + _client->getNickname() + " +Bo"; + _client->sendMessage(Message::create(reply)); + _client->sendMessage(Replies::RPL_YOUREPRIVBOT(_client)); + + map channels = Server::getInstance().getChannels(); + string chansToJoin; + for (map::iterator it = channels.begin(); + it != channels.end(); it++) { + if (!chansToJoin.empty()) + chansToJoin += ","; + chansToJoin += it->first; + } + + vector tmp; + tmp.push_back(chansToJoin); + JoinCommand j("", tmp, _client); + j.run(); +} \ No newline at end of file diff --git a/src/Commands/JoinCommand.cpp b/src/Commands/JoinCommand.cpp index 07bf3c1..b366e3a 100644 --- a/src/Commands/JoinCommand.cpp +++ b/src/Commands/JoinCommand.cpp @@ -60,6 +60,7 @@ void JoinCommand::parseParams() { } catch (Channel::WrongSyntaxChannelName&) { break; } + Server::getInstance().notifyPrivBot(chanName); } i++; } @@ -117,7 +118,7 @@ void JoinCommand::run() { parseParams(); for (size_t i = 0; i < _channels.size(); i++) { - if (_channels[i]->isKeyed()) { + if (_channels[i]->isKeyed() && !_channels[i]->isOperator(_client)) { if (i >= _keys.size() || _keys[i] != _channels[i]->getKey()) { _client->sendMessage( Replies::ERR_BADCHANNELKEY(_client, _channels[i])); diff --git a/src/Commands/KickCommand.cpp b/src/Commands/KickCommand.cpp index 4686552..34c7e23 100644 --- a/src/Commands/KickCommand.cpp +++ b/src/Commands/KickCommand.cpp @@ -29,7 +29,7 @@ void KickCommand::checkParams(Client* client, vector params) { } Channel* chan; - this->_targetNickname = toLowerCase(params[1]); + string targetNickName = toLowerCase(params[1]); string chanName = toLowerCase(params[0]); try { @@ -46,14 +46,21 @@ void KickCommand::checkParams(Client* client, vector params) { throw ClientException(); } - if (!chan->isOperator(client)) { + try { + this->_target = Server::getInstance().findClient(targetNickName); + } catch (Server::ClientNotFoundException&) { + _client->sendMessage(Replies::ERR_NOSUCHNICK(_client, targetNickName)); + return; + } + + if (!chan->isOperator(client) || chan->isOperator(_target)) { client->sendMessage(Replies::ERR_CHANOPRIVSNEEDED(client, chan)); throw ClientException(); } - if (!chan->isInChannel(_targetNickname)) { + if (!chan->isInChannel(_target)) { client->sendMessage( - Replies::ERR_USERNOTINCHANNEL(_client, _targetNickname, _channel)); + Replies::ERR_USERNOTINCHANNEL(_client, targetNickName, _channel)); throw ClientException(); } @@ -66,21 +73,10 @@ void KickCommand::checkParams(Client* client, vector params) { void KickCommand::run() { string reply; reply = ":" + _client->getSource() + " KICK " + _channel->getName() + " " + - _targetNickname + " " + _comment; + _target->getNickname() + " " + _comment; Message::create(reply); - Client* target; - - try { - target = Server::getInstance().findClient(_targetNickname); - } catch (Server::ClientNotFoundException&) { - _client->sendMessage(Replies::ERR_NOSUCHNICK(_client, _targetNickname)); - return; - } - - _channel->removeClient(target); - if (_channel->isOperator(target)) - _channel->removeOperator(target); - target->sendMessage(reply); + _channel->removeClient(_target); + _target->sendMessage(reply); Server::getInstance().sendMessage(_channel, reply, NULL); } \ No newline at end of file diff --git a/src/Commands/ModeCommand.cpp b/src/Commands/ModeCommand.cpp index cd74805..b8417d5 100644 --- a/src/Commands/ModeCommand.cpp +++ b/src/Commands/ModeCommand.cpp @@ -103,6 +103,12 @@ void ModeCommand::banMode(bool oper, size_t& p) { addResult(oper, "b", param); } +void ModeCommand::botMode(bool oper, size_t& p) { + _client->setBot(oper); + (void)p; + addResult(oper, "B", ""); +} + void ModeCommand::limitMode(bool oper, size_t& p) { if (!oper) { _channel->setMaxClients(0); @@ -267,6 +273,9 @@ void ModeCommand::executeMode() { if (unknownFlag) _client->sendMessage(Replies::ERR_UMODEUNKNOWNFLAG(_client)); + if (_modeResult.empty()) + return; + string message = ":" + _client->getNickname() + " MODE " + _params[0]; message += " " + _modeResult + _paramResult; if (_isChan) diff --git a/src/Replies.cpp b/src/Replies.cpp index f8a3757..0aab117 100644 --- a/src/Replies.cpp +++ b/src/Replies.cpp @@ -382,6 +382,8 @@ string Replies::RPL_WHOREPLY(Client* client, Client* target, Channel* channel) { flags += "G"; else flags += "H"; + if (target->isBot()) + flags += "B"; if (target->isServerOperator()) flags += "*"; if (channel) { @@ -833,8 +835,27 @@ string Replies::ERR_REGFAILED() { return Message::create(reply); } -std::string Replies::ERR_QUIT() { +string Replies::ERR_QUIT() { std::string reply; reply = "ERROR :No hard feelings, goodbye."; return Message::create(reply); +} + +string Replies::ERR_INVALIDBOTKEY(Client* client) { + string reply; + reply = "909 " + client->getNickname() + " :Bot key is invalid"; + return Message::create(reply); +} + +string Replies::RPL_YOUREPRIVBOT(Client* client) { + string reply; + reply = "910 " + client->getNickname() + " :You are now an privilegied bot"; + return Message::create(reply); +} + +string Replies::RPL_NEWCHAN(Client* client, string chanName) { + string reply; + reply = + "911 " + client->getNickname() + " " + chanName + " :has been created"; + return Message::create(reply); } \ No newline at end of file diff --git a/src/Server.cpp b/src/Server.cpp index 996bb47..4cf70cf 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -6,6 +6,7 @@ #include "Channel.hpp" #include "Client.hpp" +#include "Replies.hpp" #include "Server.hpp" #include "ServerSocket.hpp" @@ -25,11 +26,11 @@ static const string channelModes = "biklmntov"; static const string version = "1.0.0"; static const string creationDate = "27/07/2024"; static const string rplSupport1 = - "AWAYLEN=255 CASEMAPPING=ascii CHANLIMIT=#: CHANMODES=b,k,l,imnt " + "AWAYLEN=255 BOT=B CASEMAPPING=ascii CHANLIMIT=#: CHANMODES=b,k,l,imnt " "CHANNELLEN=30 CHANTYPES=# " - "ELIST= HOSTLEN=64 KICKLEN=255 MAXLIST=b:20 NICKLEN=30 PREFIX=(ov)@+ " - "STATUSMSG="; -static const string rplSupport2 = "TOPICLEN=307 USERLEN=18"; + "ELIST= HOSTLEN=64 KICKLEN=255 MAXLIST=b:20 NICKLEN=30 PREFIX=(ov)@+ "; +static const string rplSupport2 = "STATUSMSG= TOPICLEN=307 USERLEN=18"; +static const string botKey = "#WM5dal&wGPoVR"; Server::Server() {} @@ -206,6 +207,8 @@ string Server::getRplSupport1() const { return rplSupport1; } string Server::getRplSupport2() const { return rplSupport2; } +string Server::getBotKey() const { return botKey; } + size_t Server::getMaxClients() const { return _maxClients; } bool Server::isRunning() const { return this->_running; } @@ -262,6 +265,14 @@ void Server::sendMessageIfAway(map channels, string message, } } +void Server::notifyPrivBot(string chanName) { + for (vector::iterator it = _clients.begin(); it != _clients.end(); + it++) { + if ((*it)->isServerOperator() && (*it)->isBot()) + (*it)->sendMessage(Replies::RPL_NEWCHAN(*it, chanName)); + } +} + string toLowerCase(string s) { std::transform(s.begin(), s.end(), s.begin(), ::tolower); return s; diff --git a/src/main.cpp b/src/main.cpp index ee4895b..3bfb25f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include "Server.hpp" From a2eed61f6e2bf4cdd0fb4764cc73cd3776825967 Mon Sep 17 00:00:00 2001 From: Andrew Attali Date: Thu, 12 Sep 2024 15:33:31 +0200 Subject: [PATCH 2/2] fix: correct spelling in reply --- src/Replies.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Replies.cpp b/src/Replies.cpp index 0aab117..77720e7 100644 --- a/src/Replies.cpp +++ b/src/Replies.cpp @@ -849,7 +849,7 @@ string Replies::ERR_INVALIDBOTKEY(Client* client) { string Replies::RPL_YOUREPRIVBOT(Client* client) { string reply; - reply = "910 " + client->getNickname() + " :You are now an privilegied bot"; + reply = "910 " + client->getNickname() + " :You are now a privileged bot"; return Message::create(reply); }