summaryrefslogtreecommitdiff
path: root/modules/global
diff options
context:
space:
mode:
Diffstat (limited to 'modules/global')
-rw-r--r--modules/global/gl_global.cpp73
-rw-r--r--modules/global/gl_queue.cpp208
-rw-r--r--modules/global/gl_server.cpp112
-rw-r--r--modules/global/global.cpp152
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> &params) 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> &params) 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> &params) 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> &params) 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)