diff options
Diffstat (limited to 'modules/global')
| -rw-r--r-- | modules/global/gl_global.cpp | 73 | ||||
| -rw-r--r-- | modules/global/gl_queue.cpp | 208 | ||||
| -rw-r--r-- | modules/global/gl_server.cpp | 112 | ||||
| -rw-r--r-- | modules/global/global.cpp | 152 |
4 files changed, 488 insertions, 57 deletions
diff --git a/modules/global/gl_global.cpp b/modules/global/gl_global.cpp index edfaa21a5..25fd05f08 100644 --- a/modules/global/gl_global.cpp +++ b/modules/global/gl_global.cpp @@ -14,40 +14,75 @@ class CommandGLGlobal final : public Command { - ServiceReference<GlobalService> GService; +private: + ServiceReference<GlobalService> global; + + BotInfo *GetSender(CommandSource &source) + { + Reference<BotInfo> sender; + if (global) + sender = global->GetDefaultSender(); + if (!sender) + sender = source.service; + return sender; + } public: - CommandGLGlobal(Module *creator) : Command(creator, "global/global", 1, 1), GService("GlobalService", "Global") + CommandGLGlobal(Module *creator) + : Command(creator, "global/global", 0) + , global("GlobalService", "Global") { this->SetDesc(_("Send a message to all users")); - this->SetSyntax(_("\037message\037")); + this->SetSyntax(_("[\037message\037]")); } void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override { - const Anope::string &msg = params[0]; + if (!global) + { + source.Reply(SERVICE_UNAVAILABLE, source.service->nick.c_str()); + return; + } + + auto queuesize = global->CountQueue(source.nc); + if (!queuesize && params.empty()) + { + source.Reply(GLOBAL_NO_MESSAGE); + return; + } - if (!GService) - source.Reply("No global reference, is global loaded?"); + if (queuesize && !params.empty()) + { + source.Reply(GLOBAL_QUEUE_CONFLICT); + return; + } + + if (params.empty()) + { + // We are sending the message queue. + global->SendQueue(source, GetSender(source)); + } else { - Log(LOG_ADMIN, source, this); - GService->SendGlobal(NULL, source.GetNick(), msg); + // We are sending a single message. + global->SendSingle(params[0], &source, GetSender(source)); + queuesize = 1; } + + Log(LOG_ADMIN, source, this) << "to send " << queuesize << " messages to all users"; } bool OnHelp(CommandSource &source, const Anope::string &subcommand) override { - Reference<BotInfo> sender; - if (GService) - sender = GService->GetDefaultSender(); - if (!sender) - sender = source.service; - this->SendSyntax(source); source.Reply(" "); - source.Reply(_("Allows Administrators to send messages to all users on the\n" - "network. The message will be sent from the nick \002%s\002."), sender->nick.c_str()); + source.Reply(_( + "Allows sending messages to all users on the network. The message will be sent\n" + "from \002%s\002.\n" + "\n" + "You can either send a message by specifying it as a parameter or provide no\n" + "parameters to send a previously queued message.\n" + ), GetSender(source)->nick.c_str()); return true; } }; @@ -55,11 +90,13 @@ public: class GLGlobal final : public Module { +private: CommandGLGlobal commandglglobal; public: - GLGlobal(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandglglobal(this) + GLGlobal(const Anope::string &modname, const Anope::string &creator) + : Module(modname, creator, VENDOR) + , commandglglobal(this) { } diff --git a/modules/global/gl_queue.cpp b/modules/global/gl_queue.cpp new file mode 100644 index 000000000..fde15e15d --- /dev/null +++ b/modules/global/gl_queue.cpp @@ -0,0 +1,208 @@ +/* Global core functions + * + * (C) 2003-2024 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for further details. + * + * Based on the original code of Epona by Lara. + * Based on the original code of Services by Andy Church. + */ + +#include "module.h" + +#define QUEUE_EMPTY _("You have no messages queued.") + +class QueueDelCallback final + : public NumberList +{ +private: + unsigned deleted = 0; + ServiceReference<GlobalService> &global; + CommandSource &source; + +public: + QueueDelCallback(CommandSource &src, ServiceReference<GlobalService>& gl, const Anope::string &list) + : NumberList(list, true) + , global(gl) + , source(src) + { + } + + ~QueueDelCallback() override + { + switch (deleted) + { + case 0: + source.Reply(_("No matching entries in your message queue.")); + break; + case 1: + source.Reply(_("Deleted one entry from your message queue.")); + break; + default: + source.Reply(_("Deleted %u entries from your message queue."), deleted); + break; + } + } + + void HandleNumber(unsigned number) override + { + if (!number || number > global->CountQueue(source.nc)) + return; + + if (global->Unqueue(source.nc, number - 1)) + deleted++; + } +}; + +class CommandGLQueue final + : public Command +{ +private: + ServiceReference<GlobalService> global; + + void DoAdd(CommandSource &source, const Anope::string &message) + { + if (message.empty()) + { + this->OnSyntaxError(source, "ADD"); + return; + } + + auto maxqueue = Config->GetModule(this->module)->Get<size_t>("maxqueue", "10"); + if (global->CountQueue(source.nc) >= maxqueue) + { + source.Reply(_("You can not queue any more messages.")); + return; + } + + global->Queue(source.nc, message); + source.Reply(_("Your message has been queued.")); + Log(LOG_ADMIN, source, this) << "to queue: " << message; + } + + void DoClear(CommandSource &source) + { + if (!global->CountQueue(source.nc)) + { + source.Reply(_("You do not have any queued messages.")); + return; + } + + global->ClearQueue(source.nc); + source.Reply(_("Your message queue has been cleared.")); + Log(LOG_ADMIN, source, this); + } + + void DoDel(CommandSource &source, const Anope::string &what) + { + if (what.empty()) + { + this->OnSyntaxError(source, "DEL"); + return; + } + + if (!global->CountQueue(source.nc)) + { + source.Reply(QUEUE_EMPTY); + return; + } + + QueueDelCallback(source, global, what).Process(); + } + + void DoList(CommandSource &source) + { + const auto *q = global->GetQueue(source.nc); + if (!q || q->empty()) + { + source.Reply(QUEUE_EMPTY); + return; + } + + ListFormatter list(source.nc); + list.AddColumn(_("Number")).AddColumn(_("Message")); + for (size_t i = 0; i < q->size(); ++i) + { + ListFormatter::ListEntry entry; + entry["Number"] = Anope::ToString(i + 1); + entry["Message"] = (*q)[i]; + list.AddEntry(entry); + } + + std::vector<Anope::string> replies; + list.Process(replies); + for (const auto &reply : replies) + source.Reply(reply); + } + +public: + CommandGLQueue(Module *creator) + : Command(creator, "global/queue", 1, 2) + , global("GlobalService", "Global") + { + this->SetDesc(_("Manages your pending message queue.")); + this->SetSyntax(_("ADD \037message\037")); + this->SetSyntax(_("DEL \037entry-num\037")); + this->SetSyntax("LIST"); + this->SetSyntax("CLEAR"); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override + { + if (!global) + { + source.Reply(SERVICE_UNAVAILABLE, source.service->nick.c_str()); + return; + } + + const auto &cmd = params[0]; + const auto &what = params.size() > 1 ? params[1] : ""; + if (cmd.equals_ci("ADD")) + this->DoAdd(source, what); + else if (cmd.equals_ci("CLEAR")) + this->DoClear(source); + else if (cmd.equals_ci("DEL")) + this->DoDel(source, what); + else if (cmd.equals_ci("LIST")) + this->DoList(source); + else + this->OnSyntaxError(source, ""); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) override + { + this->SendSyntax(source); + source.Reply(""); + source.Reply(_( + "Allows queueing messages to send to users on the network.\n" + "\n" + "The \002QUEUE ADD\002 command adds the given message to the message queue." + "\n" + "The \002QUEUE CLEAR\002 command clears the message queue." + "\n" + "The \002QUEUE DEL\002 command removes the specified message from the message queue. The\n" + "message number can be obtained from the output of the \002QUEUE LIST\002 command." + "\n" + "The \002QUEUE LIST\002 command lists all messages that are currently in the message queue." + )); + return true; + } +}; + +class GLQueue final + : public Module +{ +private: + CommandGLQueue commandglqueue; + +public: + GLQueue(const Anope::string &modname, const Anope::string &creator) + : Module(modname, creator, VENDOR) + , commandglqueue(this) + { + + } +}; + +MODULE_INIT(GLQueue) diff --git a/modules/global/gl_server.cpp b/modules/global/gl_server.cpp new file mode 100644 index 000000000..d1f1cffe2 --- /dev/null +++ b/modules/global/gl_server.cpp @@ -0,0 +1,112 @@ +/* Global core functions + * + * (C) 2003-2024 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for further details. + * + * Based on the original code of Epona by Lara. + * Based on the original code of Services by Andy Church. + */ + +#include "module.h" + +class CommandGLServer final + : public Command +{ +private: + ServiceReference<GlobalService> global; + + BotInfo *GetSender(CommandSource &source) + { + Reference<BotInfo> sender; + if (global) + sender = global->GetDefaultSender(); + if (!sender) + sender = source.service; + return sender; + } + +public: + CommandGLServer(Module *creator) + : Command(creator, "global/server", 1) + , global("GlobalService", "Global") + { + this->SetDesc(_("Send a message to all users on a server")); + this->SetSyntax(_("\037server\037 [\037message\037]")); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override + { + if (!global) + { + source.Reply(SERVICE_UNAVAILABLE, source.service->nick.c_str()); + return; + } + + auto *server = Server::Find(params[0]); + if (!server || server == Me || server->IsJuped()) + { + source.Reply(_("Server \002%s\002 is not linked to the network."), params[0].c_str()); + return; + } + + auto queuesize = global->CountQueue(source.nc); + if (!queuesize && params.size() < 2) + { + source.Reply(GLOBAL_NO_MESSAGE); + return; + } + + if (queuesize && params.size() > 1) + { + source.Reply(GLOBAL_QUEUE_CONFLICT); + return; + } + + if (params.empty()) + { + // We are sending the message queue. + global->SendQueue(source, GetSender(source), server); + } + else + { + // We are sending a single message. + global->SendSingle(params[1], &source, GetSender(source), server); + queuesize = 1; + } + + Log(LOG_ADMIN, source, this) << "to send " << queuesize << " messages to users on " << server->GetName(); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) override + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_( + "Allows sending messages to all users on a server. The message will be sent\n" + "from \002%s\002.\n" + "\n" + "You can either send a message by specifying it as a parameter or provide no\n" + "parameters to send a previously queued message.\n" + ), GetSender(source)->nick.c_str()); + return true; + } +}; + +class GLServer final + : public Module +{ +private: + CommandGLServer commandglserver; + +public: + GLServer(const Anope::string &modname, const Anope::string &creator) + : Module(modname, creator, VENDOR) + , commandglserver(this) + { + + } +}; + +MODULE_INIT(GLServer) diff --git a/modules/global/global.cpp b/modules/global/global.cpp index a5288d754..35177dcfd 100644 --- a/modules/global/global.cpp +++ b/modules/global/global.cpp @@ -15,88 +15,162 @@ class GlobalCore final : public Module , public GlobalService { - Reference<BotInfo> Global; +private: + Reference<BotInfo> global; + PrimitiveExtensibleItem<std::vector<Anope::string>> queue; - void ServerGlobal(BotInfo *sender, Server *s, const Anope::string &message) + void ServerGlobal(BotInfo *sender, Server *server, bool children, const Anope::string &message) { - if (s != Me && !s->IsJuped()) - s->Notice(sender, message); - for (auto *link : s->GetLinks()) - this->ServerGlobal(sender, link, message); + if (server != Me && !server->IsJuped()) + server->Notice(sender, message); + + if (children) + { + for (auto *link : server->GetLinks()) + this->ServerGlobal(sender, link, true, message); + } } public: - GlobalCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR), - GlobalService(this) + GlobalCore(const Anope::string &modname, const Anope::string &creator) + : Module(modname, creator, PSEUDOCLIENT | VENDOR) + , GlobalService(this) + , queue(this, "global-queue") { } - Reference<BotInfo> GetDefaultSender() override + void ClearQueue(NickCore *nc) override { - return Global; + queue.Unset(nc); } - void SendGlobal(BotInfo *sender, const Anope::string &source, const Anope::string &message) override + Reference<BotInfo> GetDefaultSender() const override { - if (Me->GetLinks().empty()) - return; - if (!sender) - sender = Global; - if (!sender) - return; - - Anope::string rmessage; + return global; + } - if (!source.empty() && !Config->GetModule("global")->Get<bool>("anonymousglobal")) - rmessage = "[" + source + "] " + message; - else - rmessage = message; + const std::vector<Anope::string> *GetQueue(NickCore* nc) const override + { + return queue.Get(nc); + } - this->ServerGlobal(sender, Servers::GetUplink(), rmessage); + size_t Queue(NickCore *nc, const Anope::string &message) override + { + auto *q = queue.Require(nc); + q->push_back(message); + return q->size(); } void OnReload(Configuration::Conf *conf) override { - const Anope::string &glnick = conf->GetModule(this)->Get<const Anope::string>("client"); - + const auto glnick = conf->GetModule(this)->Get<const Anope::string>("client"); if (glnick.empty()) throw ConfigException(Module::name + ": <client> must be defined"); - BotInfo *bi = BotInfo::Find(glnick, true); + auto *bi = BotInfo::Find(glnick, true); if (!bi) throw ConfigException(Module::name + ": no bot named " + glnick); - Global = bi; + global = bi; } void OnRestart() override { - const Anope::string &gl = Config->GetModule(this)->Get<const Anope::string>("globaloncycledown"); - if (!gl.empty()) - this->SendGlobal(Global, "", gl); + const auto msg = Config->GetModule(this)->Get<const Anope::string>("globaloncycledown"); + if (!msg.empty()) + this->SendSingle(msg, nullptr, nullptr, nullptr); } void OnShutdown() override { - const Anope::string &gl = Config->GetModule(this)->Get<const Anope::string>("globaloncycledown"); - if (!gl.empty()) - this->SendGlobal(Global, "", gl); + const auto msg = Config->GetModule(this)->Get<const Anope::string>("globaloncycledown"); + if (!msg.empty()) + this->SendSingle(msg, nullptr, nullptr, nullptr); } void OnNewServer(Server *s) override { - const Anope::string &gl = Config->GetModule(this)->Get<const Anope::string>("globaloncycleup"); - if (!gl.empty() && !Me->IsSynced()) - s->Notice(Global, gl); + const auto msg = Config->GetModule(this)->Get<const Anope::string>("globaloncycleup"); + if (!msg.empty() && !Me->IsSynced()) + s->Notice(global, msg); } EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> ¶ms) override { - if (!params.empty() || source.c || source.service != *Global) + if (!params.empty() || source.c || source.service != *global) return EVENT_CONTINUE; - source.Reply(_("%s commands:"), Global->nick.c_str()); + + source.Reply(_("%s commands:"), global->nick.c_str()); return EVENT_CONTINUE; } + + bool SendQueue(CommandSource &source, BotInfo *sender, Server *server) override + { + // We MUST have an account. + if (!source.nc) + return false; + + // We MUST have a message queue. + auto *q = queue.Get(source.nc); + if (!q || q->empty()) + return false; + + auto success = true; + for (const auto &message : *q) + { + if (!SendSingle(message, &source, sender, server)) + { + success = false; + break; + } + } + + queue.Unset(source.nc); + return success; + } + + bool SendSingle(const Anope::string &message, CommandSource *source, BotInfo* sender, Server* server) override + { + // We MUST have a sender. + if (sender) + sender = global; + if (!sender) + return false; + + if (!server) + server = Servers::GetUplink(); + + Anope::string line; + if (source && !Config->GetModule(this)->Get<bool>("anonymousglobal")) + { + // A source is available and they're not anonymous. + line = Anope::printf("[%s] %s", source->GetNick().c_str(), message.c_str()); + } + else + { + // A source isn't available or they're anonymous. + line = message.empty() ? " " : message; + } + + if (server) + this->ServerGlobal(sender, Servers::GetUplink(), true, line); + else + this->ServerGlobal(sender, server, false, line); + return true; + } + + bool Unqueue(NickCore *nc, size_t idx) + { + auto *q = queue.Get(nc); + if (!q || idx > q->size()) + return false; + + q->erase(q->begin() + idx); + if (q->empty()) + queue.Unset(nc); + + return true; + } }; MODULE_INIT(GlobalCore) |
