summaryrefslogtreecommitdiff
path: root/modules/botserv/bot.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/botserv/bot.cpp')
-rw-r--r--modules/botserv/bot.cpp449
1 files changed, 449 insertions, 0 deletions
diff --git a/modules/botserv/bot.cpp b/modules/botserv/bot.cpp
new file mode 100644
index 000000000..21a5f6ce9
--- /dev/null
+++ b/modules/botserv/bot.cpp
@@ -0,0 +1,449 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2017 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope 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, version 2.
+ *
+ * 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 see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/botserv/bot.h"
+
+class CommandBSBot : public Command
+{
+ void DoAdd(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ const Anope::string &nick = params[1];
+ const Anope::string &user = params[2];
+ const Anope::string &host = params[3];
+ const Anope::string &real = params[4];
+
+ if (ServiceBot::Find(nick, true))
+ {
+ source.Reply(_("Bot \002{0}\002 already exists."), nick);
+ return;
+ }
+
+ Configuration::Block *networkinfo = Config->GetBlock("networkinfo");
+
+ if (nick.length() > networkinfo->Get<unsigned>("nicklen"))
+ {
+ source.Reply(_("Bot nicks may only be \002{0}\002 characters long."), networkinfo->Get<unsigned>("nicklen"));
+ return;
+ }
+
+ if (user.length() > networkinfo->Get<unsigned>("userlen"))
+ {
+ source.Reply(_("Bot idents may only be \002{0}\002 characters long."), networkinfo->Get<unsigned>("userlen"));
+ return;
+ }
+
+ if (host.length() > networkinfo->Get<unsigned>("hostlen"))
+ {
+ source.Reply(_("Bot hosts may only be \002{0}\002 characters long."), networkinfo->Get<unsigned>("hostlen"));
+ return;
+ }
+
+ if (!IRCD->IsNickValid(nick))
+ {
+ source.Reply(_("Bot nicks may only contain valid nick characters."));
+ return;
+ }
+
+ if (!IRCD->IsIdentValid(user))
+ {
+ source.Reply(_("Bot idents may only contain valid ident characters."));
+ return;
+ }
+
+ if (!IRCD->IsHostValid(host))
+ {
+ source.Reply(_("Bot hosts may only contain valid host characters."));
+ return;
+ }
+
+ /* We check whether the nick is registered, and inform the user
+ * if so. You need to drop the nick manually before you can use
+ * it as a bot nick from now on -GD
+ */
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (na)
+ {
+ source.Reply(_("\002{0}\002 is already registered!"), na->GetNick());
+ return;
+ }
+
+ User *targ = User::Find(nick, true);
+ if (targ)
+ {
+ source.Reply(_("\002{0}\002 is currently in use."), targ->nick);
+ return;
+ }
+
+ ServiceBot *bi = new ServiceBot(nick, user, host, real);
+
+ BotInfo *botinfo = Serialize::New<BotInfo *>();
+ botinfo->SetNick(nick);
+ botinfo->SetUser(user);
+ botinfo->SetHost(host);
+ botinfo->SetRealName(real);
+ botinfo->SetCreated(Anope::CurTime);
+
+ bi->bi = botinfo;
+ botinfo->bot = bi;
+
+ logger.Admin(source, _("{source} used {command} to add bot {0} {1}"), bi->GetMask(), bi->realname);
+
+ source.Reply(_("\002{0}!{1}@{2}\002 (\002{3}\002) added to the bot list."), bi->nick, bi->GetIdent(), bi->host, bi->realname);
+
+ EventManager::Get()->Dispatch(&Event::BotCreate::OnBotCreate, bi);
+ }
+
+ void DoChange(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ const Anope::string &oldnick = params[1];
+ const Anope::string &nick = params.size() > 2 ? params[2] : "";
+ const Anope::string &user = params.size() > 3 ? params[3] : "";
+ const Anope::string &host = params.size() > 4 ? params[4] : "";
+ const Anope::string &real = params.size() > 5 ? params[5] : "";
+
+ if (oldnick.empty() || nick.empty())
+ {
+ this->OnSyntaxError(source, "CHANGE");
+ return;
+ }
+
+ ServiceBot *bi = ServiceBot::Find(oldnick, true);
+ if (!bi)
+ {
+ source.Reply(_("Bot \002{0}\002 does not exist."), oldnick);
+ return;
+ }
+
+ if (bi->bi && bi->bi->conf)
+ {
+ source.Reply(_("Bot \002{0}\002 is not changeable because it is configured in services configuration."), bi->nick.c_str());
+ return;
+ }
+
+ Configuration::Block *networkinfo = Config->GetBlock("networkinfo");
+
+ if (nick.length() > networkinfo->Get<unsigned>("nicklen"))
+ {
+ source.Reply(_("Bot nicknames may only be \002{0}\002 characters long."), networkinfo->Get<unsigned>("nicklen"));
+ return;
+ }
+
+ if (user.length() > networkinfo->Get<unsigned>("userlen"))
+ {
+ source.Reply(_("Bot usernames may only be \002{0}\002 characters long."), networkinfo->Get<unsigned>("userlen"));
+ return;
+ }
+
+ if (host.length() > networkinfo->Get<unsigned>("hostlen"))
+ {
+ source.Reply(_("Bot hostnames may only be \002{0}\002 characters long."), networkinfo->Get<unsigned>("hostlen"));
+ return;
+ }
+
+ /* Checks whether there *are* changes.
+ * Case sensitive because we may want to change just the case.
+ * And we must finally check that the nick is not already
+ * taken by another bot.
+ */
+ if (nick.equals_cs(bi->nick)
+ && (user.empty() || user.equals_cs(bi->GetIdent()))
+ && (host.empty() || host.equals_cs(bi->host))
+ && (real.empty() || real.equals_cs(bi->realname)))
+ {
+ source.Reply(_("There is no difference between the current settings and the new settings."));
+ return;
+ }
+
+ if (!IRCD->IsNickValid(nick))
+ {
+ source.Reply(_("Bot nicknames may only contain valid nickname characters."));
+ return;
+ }
+
+ if (!user.empty() && !IRCD->IsIdentValid(user))
+ {
+ source.Reply(_("Bot uesrnames may only contain valid username characters."));
+ return;
+ }
+
+ if (!host.empty() && !IRCD->IsHostValid(host))
+ {
+ source.Reply(_("Bot hostnames may only contain valid hostname characters."));
+ return;
+ }
+
+ ServiceBot *newbi = ServiceBot::Find(nick, true);
+ if (newbi && bi != newbi)
+ {
+ source.Reply(_("Bot \002{0}\002 already exists."), newbi->nick);
+ return;
+ }
+
+ User *target = User::Find(nick, true);
+ if (target)
+ {
+ source.Reply(_("\002{0}\002 is currently in use."), target->nick);
+ return;
+ }
+
+ if (!nick.equals_ci(bi->nick))
+ {
+ /* We check whether the nick is registered, and inform the user
+ * if so. You need to drop the nick manually before you can use
+ * it as a bot nick from now on -GD
+ */
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (na)
+ {
+ source.Reply(_("\002{0}\002 is already registered."), na->GetNick());
+ return;
+ }
+
+#warning "xline on the stack"
+ /* The new nick is really different, so we remove the Q line for the old nick. */
+ //XLine x_del(bi->nick);
+ //IRCD->SendSQLineDel(&x_del);
+
+ /* Add a Q line for the new nick */
+ //XLine x(nick, "Reserved for services");
+ //IRCD->SendSQLine(NULL, &x);
+ }
+
+ if (!user.empty())
+ {
+ IRCD->SendQuit(bi, "Quit: Be right back");
+ bi->introduced = false;
+ }
+ else
+ {
+ IRCD->Send<messages::NickChange>(bi, nick, Anope::CurTime);
+ bi->timestamp = Anope::CurTime;
+ }
+
+ if (!nick.equals_cs(bi->nick))
+ {
+ bi->SetNewNick(nick);
+ if (bi->bi != nullptr)
+ bi->bi->SetNick(nick);
+ }
+
+ if (!user.equals_cs(bi->GetIdent()))
+ {
+ bi->SetIdent(user);
+ if (bi->bi != nullptr)
+ bi->bi->SetUser(user);
+ }
+ if (!host.equals_cs(bi->host))
+ {
+ bi->host = host;
+ if (bi->bi != nullptr)
+ bi->bi->SetHost(host);
+ }
+ if (real.equals_cs(bi->realname))
+ {
+ bi->realname = real;
+ if (bi->bi != nullptr)
+ bi->bi->SetRealName(real);
+ }
+
+ if (!user.empty())
+ bi->OnKill();
+
+ logger.Admin(source, _("{source} used {command} to change bot {0} to {1} {2}"),
+ oldnick, bi->GetMask(), bi->realname);
+
+ source.Reply(_("Bot \002{0}\002 has been changed to \002{1}!{2}@{3}\002 (\002{4}\002)."), oldnick, bi->nick, bi->GetIdent(), bi->host, bi->realname);
+
+ EventManager::Get()->Dispatch(&Event::BotChange::OnBotChange, bi);
+ }
+
+ void DoDel(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ const Anope::string &nick = params[1];
+
+ if (nick.empty())
+ {
+ this->OnSyntaxError(source, "DEL");
+ return;
+ }
+
+ ServiceBot *bi = ServiceBot::Find(nick, true);
+ if (!bi)
+ {
+ source.Reply(_("Bot \002{0}\002 does not exist."), nick);
+ return;
+ }
+
+ if (bi->bi && bi->bi->conf)
+ {
+ source.Reply(_("Bot \002{0}\002 is can not be deleted because it is configured in services configuration."), bi->nick);
+ return;
+ }
+
+ EventManager::Get()->Dispatch(&Event::BotDelete::OnBotDelete, bi);
+
+ logger.Admin(source, _("{source} used {command} to delete bot {0}"), bi->nick);
+
+ source.Reply(_("Bot \002{0}\002 has been deleted."), bi->nick);
+ delete bi;
+ }
+
+ public:
+ CommandBSBot(Module *creator) : Command(creator, "botserv/bot", 1, 6)
+ {
+ this->SetDesc(_("Maintains network bot list"));
+ this->SetSyntax(_("\002ADD \037nicknae\037 \037username\037 \037hostname\037 \037realname\037\002"));
+ this->SetSyntax(_("\002CHANGE \037oldnickname\037 \037newnickname\037 [\037username\037 [\037hostname\037 [\037realname\037]]]\002"));
+ this->SetSyntax(_("\002DEL \037nickname\037\002"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &cmd = params[0];
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ if (cmd.equals_ci("ADD"))
+ {
+ if (!source.HasCommand("botserv/bot/add"))
+ {
+ source.Reply(_("Access denied. You do not have access to the operator command \002{0}\002."), "botserv/bot/add");
+ return;
+ }
+
+ // ADD nick user host real - 5
+ if (params.size() < 5)
+ {
+ this->OnSyntaxError(source, "ADD");
+ return;
+ }
+
+ std::vector<Anope::string> tempparams = params;
+ // ADD takes less params than CHANGE, so we need to take 6 if given and append it with a space to 5.
+ if (tempparams.size() >= 6)
+ tempparams[4] = tempparams[4] + " " + tempparams[5];
+
+ this->DoAdd(source, tempparams);
+ }
+ else if (cmd.equals_ci("CHANGE"))
+ {
+ // CHANGE oldn newn user host real - 6
+ // but only oldn and newn are required
+ if (!source.HasCommand("botserv/bot/change"))
+ {
+ source.Reply(_("Access denied. You do not have access to the operator command \002{0}\002."), "botserv/bot/change");
+ return;
+ }
+
+ if (params.size() < 3)
+ {
+ this->OnSyntaxError(source, "CHANGE");
+ return;
+ }
+
+ this->DoChange(source, params);
+ }
+ else if (cmd.equals_ci("DEL"))
+ {
+ // DEL nick
+ if (!source.HasCommand("botserv/bot/del"))
+ {
+ source.Reply(_("Access denied. You do not have access to the operator command \002{0}\002."), "botserv/bot/del");
+ return;
+ }
+
+ if (params.size() < 1)
+ {
+ this->OnSyntaxError(source, "DEL");
+ return;
+ }
+
+ this->DoDel(source, params);
+ }
+ else
+ {
+ this->OnSyntaxError(source, "");
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ if (subcommand.equals_ci("ADD"))
+ source.Reply(_("\002{command} ADD\002 adds a bot with the given \037nickname\037, \037username\037, \037hostname\037 and \037realname\037."
+ " You can not create a bot with a nickname that is currently registered. If an unregistered user is currently using the nick, they will be killed.\n"
+ " Once a bot is created, users will be able to assign the bot to their channels. This command requires the opererator privilege for command \002{0}\002."
+ "\n"
+ "Example:\n"
+ " {command} ADD Botox Botox services.anope.org Botox\n"
+ " Adds a service bot with nickname \"Botox\", username \"Botox\", hostname \"services.anope.org\", and realname \"Botox\" to the bot list."),
+ "botserv/bot/add", "command"_kw = source.GetCommand());
+ else if (subcommand.equals_ci("CHANGE"))
+ source.Reply(_("\002{command} CHANGE\002 allows changing the \037nickname\037, \037username\037, \037hostname\037 and \037realname\037 of bot \037oldnickname\037."
+ " If a new username, hostname, or realname is specified, then the bot \037nickname\037 will quit and rejoin all of its channels using the new mask."
+ " Otherwise, the bot simply change nick to \037newnickname\037. All settings on the bot, such as channels and no-bot, are retained."
+ " This command requires the operator privilege for command \002{0}\002."
+ "\n"
+ "Example:\n"
+ " {command} CHANGE Botox peer connection reset.by peer\n"
+ " Renames the bot \"Botox\" to \"peer\" with the given username, hostname, and realname."),
+ "botserv/bot/change", "command"_kw = source.GetCommand());
+ else if (subcommand.equals_ci("DEL"))
+ source.Reply(_("\002{command} DEL\002 removes the bot \037nickname\037 from the bot list. The bot will quit from any channels it is in, and will not be replaced."
+ " This command requires the operator privilege for command \002{0}\002.\n"
+ "\n"
+ "Example:\n"
+ " {command} DEL peer\n"
+ " Removes the bot \"peer\" from the bot list."),
+ "botserv/bot/del", "command"_kw = source.GetCommand());
+ else
+ {
+ source.Reply(_("Allows Services Operators to create, modify, and delete bots that users will be able to use on their channels."));
+
+ CommandInfo *help = source.service->FindCommand("generic/help");
+ if (help)
+ source.Reply(_("\n"
+ "The \002ADD\002 command adds a bot with the given \037nickname\037, \037username\037, \037hostname\037 and \037realname\037 to the bot list.\n"
+ "\002{msg}{service} {help} {command} ADD\002 for more information.\n"
+ "\n"
+ "The \002CHANGE\002 command allows changing the \037nickname\037, \037username\037, \037hostname\037 and \037realname\037 of bot \037oldnickname\037.\n"
+ "\002{msg}{service} {help} {command} CHANGE\002 for more information.\n"
+ "\n"
+ "The \002{command} DEL\002 removes the bot \037nickname\037 from the bot list.\n"
+ "\002{msg}{service} {help} {command} DEL\002 for more information."),
+ "msg"_kw = Config->StrictPrivmsg, "help"_kw = help->cname, "command"_kw = source.GetCommand());
+ }
+ return true;
+ }
+};
+
+class BSBot : public Module
+{
+ CommandBSBot commandbsbot;
+
+ public:
+ BSBot(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandbsbot(this)
+ {
+
+ }
+};
+
+MODULE_INIT(BSBot)