summaryrefslogtreecommitdiff
path: root/modules/memoserv/memoserv.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/memoserv/memoserv.cpp')
-rw-r--r--modules/memoserv/memoserv.cpp237
1 files changed, 237 insertions, 0 deletions
diff --git a/modules/memoserv/memoserv.cpp b/modules/memoserv/memoserv.cpp
new file mode 100644
index 000000000..3b2a521c7
--- /dev/null
+++ b/modules/memoserv/memoserv.cpp
@@ -0,0 +1,237 @@
+/* MemoServ 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 MemoServCore final
+ : public Module
+ , public MemoServService
+{
+ Reference<BotInfo> MemoServ;
+
+ static bool SendMemoMail(NickCore *nc, MemoInfo *mi, Memo *m)
+ {
+ Anope::string subject = Language::Translate(nc, Config->GetBlock("mail")->Get<const Anope::string>("memo_subject").c_str()),
+ message = Language::Translate(nc, Config->GetBlock("mail")->Get<const Anope::string>("memo_message").c_str());
+
+ subject = subject.replace_all_cs("%n", nc->display);
+ subject = subject.replace_all_cs("%s", m->sender);
+ subject = subject.replace_all_cs("%d", Anope::ToString(mi->GetIndex(m) + 1));
+ subject = subject.replace_all_cs("%t", m->text);
+ subject = subject.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname"));
+
+ message = message.replace_all_cs("%n", nc->display);
+ message = message.replace_all_cs("%s", m->sender);
+ message = message.replace_all_cs("%d", Anope::ToString(mi->GetIndex(m) + 1));
+ message = message.replace_all_cs("%t", m->text);
+ message = message.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname"));
+
+ return Mail::Send(nc, subject, message);
+ }
+
+public:
+ MemoServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR),
+ MemoServService(this)
+ {
+ }
+
+ MemoResult Send(const Anope::string &source, const Anope::string &target, const Anope::string &message, bool force) override
+ {
+ bool ischan;
+ MemoInfo *mi = MemoInfo::GetMemoInfo(target, ischan);
+
+ if (mi == NULL)
+ return MEMO_INVALID_TARGET;
+
+ Anope::string sender_display = source;
+
+ User *sender = User::Find(source, true);
+ if (sender != NULL)
+ {
+ if (!sender->HasPriv("memoserv/no-limit") && !force)
+ {
+ time_t send_delay = Config->GetModule("memoserv")->Get<time_t>("senddelay");
+ if (send_delay > 0 && sender->lastmemosend + send_delay > Anope::CurTime)
+ return MEMO_TOO_FAST;
+ else if (!mi->memomax)
+ return MEMO_TARGET_FULL;
+ else if (mi->memomax > 0 && mi->memos->size() >= static_cast<unsigned>(mi->memomax))
+ return MEMO_TARGET_FULL;
+ else if (mi->HasIgnore(sender))
+ return MEMO_SUCCESS;
+ }
+
+ NickCore *acc = sender->Account();
+ if (acc != NULL)
+ {
+ sender_display = acc->display;
+ }
+ }
+
+ if (sender != NULL)
+ sender->lastmemosend = Anope::CurTime;
+
+ auto *m = new Memo();
+ m->mi = mi;
+ mi->memos->push_back(m);
+ m->owner = target;
+ m->sender = sender_display;
+ m->time = Anope::CurTime;
+ m->text = message;
+ m->unread = true;
+
+ FOREACH_MOD(OnMemoSend, (source, target, mi, m));
+
+ if (ischan)
+ {
+ ChannelInfo *ci = ChannelInfo::Find(target);
+
+ if (ci->c)
+ {
+ for (const auto &[_, cu] : ci->c->users)
+ {
+ if (ci->AccessFor(cu->user).HasPriv("MEMO"))
+ {
+ if (cu->user->Account() && cu->user->Account()->HasExt("MEMO_RECEIVE"))
+ cu->user->SendMessage(MemoServ, MEMO_NEW_X_MEMO_ARRIVED, ci->name.c_str(), Config->StrictPrivmsg.c_str(), MemoServ->nick.c_str(), ci->name.c_str(), mi->memos->size());
+ }
+ }
+ }
+ }
+ else
+ {
+ NickCore *nc = NickAlias::Find(target)->nc;
+
+ if (nc->HasExt("MEMO_RECEIVE"))
+ {
+ for (auto *na : *nc->aliases)
+ {
+ User *user = User::Find(na->nick, true);
+ if (user && user->IsIdentified())
+ user->SendMessage(MemoServ, MEMO_NEW_MEMO_ARRIVED, source.c_str(), Config->StrictPrivmsg.c_str(), MemoServ->nick.c_str(), mi->memos->size());
+ }
+ }
+
+ /* let's get out the mail if set in the nickcore - certus */
+ if (nc->HasExt("MEMO_MAIL"))
+ SendMemoMail(nc, mi, m);
+ }
+
+ return MEMO_SUCCESS;
+ }
+
+ void Check(User *u) override
+ {
+ const NickCore *nc = u->Account();
+ if (!nc)
+ return;
+
+ unsigned i = 0, end = nc->memos.memos->size(), newcnt = 0;
+ for (; i < end; ++i)
+ if (nc->memos.GetMemo(i)->unread)
+ ++newcnt;
+ if (newcnt > 0)
+ u->SendMessage(MemoServ, newcnt == 1 ? _("You have 1 new memo.") : _("You have %d new memos."), newcnt);
+ if (nc->memos.memomax > 0 && nc->memos.memos->size() >= static_cast<unsigned>(nc->memos.memomax))
+ {
+ if (nc->memos.memos->size() > static_cast<unsigned>(nc->memos.memomax))
+ u->SendMessage(MemoServ, _("You are over your maximum number of memos (%d). You will be unable to receive any new memos until you delete some of your current ones."), nc->memos.memomax);
+ else
+ u->SendMessage(MemoServ, _("You have reached your maximum number of memos (%d). You will be unable to receive any new memos until you delete some of your current ones."), nc->memos.memomax);
+ }
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ const Anope::string &msnick = conf->GetModule(this)->Get<const Anope::string>("client");
+
+ if (msnick.empty())
+ throw ConfigException(Module::name + ": <client> must be defined");
+
+ BotInfo *bi = BotInfo::Find(msnick, true);
+ if (!bi)
+ throw ConfigException(Module::name + ": no bot named " + msnick);
+
+ MemoServ = bi;
+ }
+
+ void OnNickCoreCreate(NickCore *nc) override
+ {
+ nc->memos.memomax = Config->GetModule(this)->Get<int>("maxmemos");
+ }
+
+ void OnCreateChan(ChannelInfo *ci) override
+ {
+ ci->memos.memomax = Config->GetModule(this)->Get<int>("maxmemos");
+ }
+
+ void OnBotDelete(BotInfo *bi) override
+ {
+ if (bi == MemoServ)
+ MemoServ = NULL;
+ }
+
+ void OnNickIdentify(User *u) override
+ {
+ this->Check(u);
+ }
+
+ void OnJoinChannel(User *u, Channel *c) override
+ {
+ if (c->ci && !c->ci->memos.memos->empty() && c->ci->AccessFor(u).HasPriv("MEMO"))
+ {
+ if (c->ci->memos.memos->size() == 1)
+ u->SendMessage(MemoServ, _("There is \002%zu\002 memo on channel %s."), c->ci->memos.memos->size(), c->ci->name.c_str());
+ else
+ u->SendMessage(MemoServ, _("There are \002%zu\002 memos on channel %s."), c->ci->memos.memos->size(), c->ci->name.c_str());
+ }
+ }
+
+ void OnUserAway(User *u, const Anope::string &message) override
+ {
+ if (message.empty())
+ this->Check(u);
+ }
+
+ void OnNickUpdate(User *u) override
+ {
+ this->Check(u);
+ }
+
+ void OnUserConnect(User *user, bool &exempt) override
+ {
+ this->Check(user);
+ }
+
+ EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!params.empty() || source.c || source.service != *MemoServ)
+ return EVENT_CONTINUE;
+ source.Reply(_("\002%s\002 is a utility allowing IRC users to send short\n"
+ "messages to other IRC users, whether they are online at\n"
+ "the time or not, or to channels(*). Both the sender's\n"
+ "nickname and the target nickname or channel must be\n"
+ "registered in order to send a memo.\n"
+ "%s's commands include:"), MemoServ->nick.c_str(), MemoServ->nick.c_str());
+ return EVENT_CONTINUE;
+ }
+
+ void OnPostHelp(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!params.empty() || source.c || source.service != *MemoServ)
+ return;
+ source.Reply(_(" \n"
+ "Type \002%s%s HELP \037command\037\002 for help on any of the\n"
+ "above commands."), Config->StrictPrivmsg.c_str(), MemoServ->nick.c_str());
+ }
+};
+
+MODULE_INIT(MemoServCore)