summaryrefslogtreecommitdiff
path: root/modules/memoserv
diff options
context:
space:
mode:
authorAdam <Adam@anope.org>2015-12-14 16:52:24 -0500
committerAdam <Adam@anope.org>2015-12-14 16:52:24 -0500
commit6302f980fe6dad83aab7d2dc1046dadea5ffd939 (patch)
tree4c27d33f55fc32ce34827977932dac4b3e5ceedd /modules/memoserv
parent64dac60071fab652745a6e7a06cf6b7bdbbd3625 (diff)
New source tree structure for modules. From commands/cs_access => chanserv/access etc.
Diffstat (limited to 'modules/memoserv')
-rw-r--r--modules/memoserv/CMakeLists.txt1
-rw-r--r--modules/memoserv/cancel.cpp91
-rw-r--r--modules/memoserv/check.cpp80
-rw-r--r--modules/memoserv/del.cpp148
-rw-r--r--modules/memoserv/ignore.cpp147
-rw-r--r--modules/memoserv/info.cpp203
-rw-r--r--modules/memoserv/list.cpp163
-rw-r--r--modules/memoserv/main/CMakeLists.txt1
-rw-r--r--modules/memoserv/main/ignore.cpp27
-rw-r--r--modules/memoserv/main/ignore.h15
-rw-r--r--modules/memoserv/main/ignoretype.cpp10
-rw-r--r--modules/memoserv/main/ignoretype.h10
-rw-r--r--modules/memoserv/main/memo.cpp72
-rw-r--r--modules/memoserv/main/memo.h29
-rw-r--r--modules/memoserv/main/memoinfo.cpp63
-rw-r--r--modules/memoserv/main/memoinfo.h23
-rw-r--r--modules/memoserv/main/memoinfotype.cpp10
-rw-r--r--modules/memoserv/main/memoinfotype.h10
-rw-r--r--modules/memoserv/main/memoserv.cpp298
-rw-r--r--modules/memoserv/main/memotype.cpp14
-rw-r--r--modules/memoserv/main/memotype.h13
-rw-r--r--modules/memoserv/read.cpp203
-rw-r--r--modules/memoserv/rsend.cpp105
-rw-r--r--modules/memoserv/send.cpp81
-rw-r--r--modules/memoserv/sendall.cpp60
-rw-r--r--modules/memoserv/set.cpp314
-rw-r--r--modules/memoserv/staff.cpp57
27 files changed, 2248 insertions, 0 deletions
diff --git a/modules/memoserv/CMakeLists.txt b/modules/memoserv/CMakeLists.txt
new file mode 100644
index 000000000..cd225a94d
--- /dev/null
+++ b/modules/memoserv/CMakeLists.txt
@@ -0,0 +1 @@
+build_modules(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/memoserv/cancel.cpp b/modules/memoserv/cancel.cpp
new file mode 100644
index 000000000..e3da82581
--- /dev/null
+++ b/modules/memoserv/cancel.cpp
@@ -0,0 +1,91 @@
+/* MemoServ core functions
+ *
+ * (C) 2003-2014 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"
+#include "modules/memoserv.h"
+
+class CommandMSCancel : public Command
+{
+ public:
+ CommandMSCancel(Module *creator) : Command(creator, "memoserv/cancel", 1, 1)
+ {
+ this->SetDesc(_("Cancel the last memo you sent"));
+ this->SetSyntax(_("{\037user\037 | \037channel\037}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ const Anope::string &nname = params[0];
+
+ bool ischan, isregistered;
+ MemoServ::MemoInfo *mi = MemoServ::service->GetMemoInfo(name, ischan, isregistered, false);
+
+ if (!isregistered)
+ {
+ if (ischan)
+ source.Reply(_("Channel \002{0}\002 isn't registered."), nname);
+ else
+ source.Reply(_("\002{0}\002 isn't registered."), nname);
+ }
+ else
+ {
+ ChanServ::Channel *ci = NULL;
+ NickServ::Nick *na = NULL;
+ if (ischan)
+ ci = ChanServ::Find(nname);
+ else
+ na = NickServ::FindNick(nname);
+ if (mi)
+ {
+ auto memos = mi->GetMemos();
+ for (int i = memos.size() - 1; i >= 0; --i)
+ {
+ MemoServ::Memo *m = memos[i];
+ if (m->GetUnread() && source.nc->GetDisplay().equals_ci(m->GetSender()))
+ {
+ if (MemoServ::Event::OnMemoDel)
+ MemoServ::Event::OnMemoDel(&MemoServ::Event::MemoDel::OnMemoDel, ischan ? ci->GetName() : na->GetAccount()->GetDisplay(), mi, m);
+ mi->Del(i);
+ source.Reply(_("Your last memo to \002{0}\002 has been cancelled."), nname);
+ return;
+ }
+ }
+ }
+
+ source.Reply(_("No memo to \002{0}\002 was cancelable."), nname);
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Cancels the last memo you sent to \037user\037, provided it has not yet been read."));
+ return true;
+ }
+};
+
+class MSCancel : public Module
+{
+ CommandMSCancel commandmscancel;
+
+ public:
+ MSCancel(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandmscancel(this)
+ {
+ }
+};
+
+MODULE_INIT(MSCancel)
diff --git a/modules/memoserv/check.cpp b/modules/memoserv/check.cpp
new file mode 100644
index 000000000..75a8ee3d0
--- /dev/null
+++ b/modules/memoserv/check.cpp
@@ -0,0 +1,80 @@
+/* MemoServ core functions
+ *
+ * (C) 2003-2014 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 CommandMSCheck : public Command
+{
+ public:
+ CommandMSCheck(Module *creator) : Command(creator, "memoserv/check", 1, 1)
+ {
+ this->SetDesc(_("Checks if last memo to a user was read"));
+ this->SetSyntax(_("\037user\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &recipient = params[0];
+
+ bool found = false;
+
+ NickServ::Nick *na = NickServ::FindNick(recipient);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), recipient);
+ return;
+ }
+
+ MemoServ::MemoInfo *mi = na->GetAccount()->GetMemos();
+
+ if (mi)
+ /* find the last memo */
+ for (unsigned i = mi->GetMemos().size(); i > 0; --i)
+ {
+ MemoServ::Memo *m = mi->GetMemo(i - 1);
+ NickServ::Nick *na2 = NickServ::FindNick(m->GetSender());
+
+ if (na2 != NULL && na2->GetAccount() == source.GetAccount())
+ {
+ found = true; /* Yes, we've found the memo */
+
+ if (m->GetUnread())
+ source.Reply(_("The last memo you sent to \002{0}\002 (sent on \002{1}\002) has not yet been read."), na->GetNick(), Anope::strftime(m->GetTime(), source.GetAccount()));
+ else
+ source.Reply(_("The last memo you sent to \002{0}\002 (sent on \002{1}\002) has been read."), na->GetNick(), Anope::strftime(m->GetTime(), source.GetAccount()));
+ break;
+ }
+ }
+
+ if (!found)
+ source.Reply(_("\002{0}\002 doesn't have a memo from you."), na->GetNick());
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Checks whether the last memo you sent to \037user\037 has been read or not."));
+ return true;
+ }
+};
+
+class MSCheck : public Module
+{
+ CommandMSCheck commandmscheck;
+
+ public:
+ MSCheck(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandmscheck(this)
+ {
+
+ }
+};
+
+MODULE_INIT(MSCheck)
diff --git a/modules/memoserv/del.cpp b/modules/memoserv/del.cpp
new file mode 100644
index 000000000..79af11adc
--- /dev/null
+++ b/modules/memoserv/del.cpp
@@ -0,0 +1,148 @@
+/* MemoServ core functions
+ *
+ * (C) 2003-2014 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"
+#include "modules/memoserv.h"
+
+class CommandMSDel : public Command
+{
+ public:
+ CommandMSDel(Module *creator) : Command(creator, "memoserv/del", 0, 2)
+ {
+ this->SetDesc(_("Delete a memo or memos"));
+ this->SetSyntax(_("[\037channel\037] {\037num\037 | \037list\037 | LAST | ALL}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ MemoServ::MemoInfo *mi;
+ ChanServ::Channel *ci = NULL;
+ Anope::string numstr = !params.empty() ? params[0] : "", chan;
+
+ if (!numstr.empty() && numstr[0] == '#')
+ {
+ chan = numstr;
+ numstr = params.size() > 1 ? params[1] : "";
+
+ ci = ChanServ::Find(chan);
+ if (!ci)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ if (!source.AccessFor(ci).HasPriv("MEMO"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "MEMO", ci->GetName());
+ return;
+ }
+
+ mi = ci->GetMemos();
+ }
+ else
+ mi = source.nc->GetMemos();
+
+ if (numstr.empty() || (!isdigit(numstr[0]) && !numstr.equals_ci("ALL") && !numstr.equals_ci("LAST")))
+ {
+ this->OnSyntaxError(source, numstr);
+ return;
+ }
+
+ if (!mi || mi->GetMemos().empty())
+ {
+ if (!chan.empty())
+ source.Reply(_("\002{0}\002 has no memos."), chan);
+ else
+ source.Reply(_("You have no memos."));
+ return;
+ }
+
+ auto memos = mi->GetMemos();
+
+ if (isdigit(numstr[0]))
+ {
+ NumberList(numstr, true,
+ [&](unsigned int number)
+ {
+ if (!number || number > memos.size())
+ return;
+
+ if (MemoServ::Event::OnMemoDel)
+ MemoServ::Event::OnMemoDel(&MemoServ::Event::MemoDel::OnMemoDel, ci ? ci->GetName() : source.nc->GetDisplay(), mi, mi->GetMemo(number - 1));
+
+ mi->Del(number - 1);
+ source.Reply(_("Memo \002{0}\002 has been deleted."), number);
+ },
+ [](){});
+ }
+ else if (numstr.equals_ci("LAST"))
+ {
+ /* Delete last memo. */
+ if (MemoServ::Event::OnMemoDel)
+ MemoServ::Event::OnMemoDel(&MemoServ::Event::MemoDel::OnMemoDel, ci ? ci->GetName() : source.nc->GetDisplay(), mi, mi->GetMemo(memos.size() - 1));
+ mi->Del(memos.size() - 1);
+ source.Reply(_("Memo \002{0}\002 has been deleted."), memos.size() + 1);
+ }
+ else
+ {
+ /* Delete all memos. */
+ std::for_each(memos.begin(), memos.end(),
+ [&](MemoServ::Memo *m)
+ {
+ if (MemoServ::Event::OnMemoDel)
+ MemoServ::Event::OnMemoDel(&MemoServ::Event::MemoDel::OnMemoDel, ci ? ci->GetName() : source.nc->GetDisplay(), mi, m);
+ delete m;
+ });
+ if (!chan.empty())
+ source.Reply(_("All memos for channel \002{0}\002 have been deleted."), chan);
+ else
+ source.Reply(_("All of your memos have been deleted."));
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Deletes the specified memo or memos."
+ " You can supply multiple memo numbers or ranges of numbers instead of a single number, as in the second example below."
+ " If \002LAST\002 is given, the last memo will be deleted."
+ " If \002ALL\002 is given, deletes all of your memos.\n"
+ "\n"
+ "Examples:\n"
+ "\n"
+ " {0} 1\n"
+ " Deletes your first memo.\n"
+ "\n"
+ " {0} 2-5,7-9\n"
+ " Deletes memos numbered 2 through 5 and 7 through 9."),
+ source.command);
+ return true;
+ }
+};
+
+class MSDel : public Module
+{
+ CommandMSDel commandmsdel;
+
+ public:
+ MSDel(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandmsdel(this)
+ {
+
+ }
+};
+
+MODULE_INIT(MSDel)
diff --git a/modules/memoserv/ignore.cpp b/modules/memoserv/ignore.cpp
new file mode 100644
index 000000000..c454444ea
--- /dev/null
+++ b/modules/memoserv/ignore.cpp
@@ -0,0 +1,147 @@
+/* MemoServ core functions
+ *
+ * (C) 2003-2014 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 CommandMSIgnore : public Command
+{
+ public:
+ CommandMSIgnore(Module *creator) : Command(creator, "memoserv/ignore", 1, 3)
+ {
+ this->SetDesc(_("Manage the memo ignore list"));
+ this->SetSyntax(_("[\037channel\037] ADD \037entry\037"));
+ this->SetSyntax(_("[\037channel\037] DEL \037entry\037"));
+ this->SetSyntax(_("[\037channel\037] LIST"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ if (!MemoServ::service)
+ return;
+
+ Anope::string channel = params[0];
+ Anope::string command = (params.size() > 1 ? params[1] : "");
+ Anope::string param = (params.size() > 2 ? params[2] : "");
+
+ if (channel[0] != '#')
+ {
+ param = command;
+ command = channel;
+ channel = source.GetNick();
+ }
+
+ bool ischan, isregistered;
+ MemoServ::MemoInfo *mi = MemoServ::service->GetMemoInfo(channel, ischan, isregistered, true);
+ ChanServ::Channel *ci = ChanServ::Find(channel);
+ if (!isregistered)
+ {
+ if (ischan)
+ source.Reply(_("Channel \002{0}\002 isn't registered."), channel);
+ else
+ source.Reply(_("\002{0}\002 isn't registered."), channel);
+ return;
+ }
+
+ if (ischan && !source.AccessFor(ci).HasPriv("MEMO"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "MEMO", ci->GetName());
+ return;
+ }
+
+ auto ignores = mi->GetIgnores();
+
+ if (command.equals_ci("ADD") && !param.empty())
+ {
+ if (ignores.size() >= Config->GetModule(this->owner)->Get<unsigned>("max", "32"))
+ {
+ source.Reply(_("Sorry, the memo ignore list for \002{0}\002 is full."), channel);
+ return;
+ }
+
+ for (MemoServ::Ignore *ign : ignores)
+ if (ign->GetMask().equals_ci(param))
+ {
+ source.Reply(_("\002{0}\002 is already on your memo ignore list."), param);
+ return;
+ }
+
+ MemoServ::Ignore *ign = MemoServ::service->CreateIgnore();
+ ign->SetMemoInfo(mi);
+ ign->SetMask(param);
+
+ source.Reply(_("\002{0}\002 has been added to your memo ignore list."), param);
+ }
+ else if (command.equals_ci("DEL") && !param.empty())
+ {
+ for (MemoServ::Ignore *ign : ignores)
+ if (ign->GetMask().equals_ci(param))
+ {
+ delete ign;
+ source.Reply(_("\002{0}\002 has been removed from your memo ignore list."), param);
+ return;
+ }
+
+ source.Reply(_("\002{0}\002 is not on your memo ignore list."), param);
+ }
+ else if (command.equals_ci("LIST"))
+ {
+ if (ignores.empty())
+ {
+ source.Reply(_("Your memo ignore list is empty."));
+ return;
+ }
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Mask"));
+ for (MemoServ::Ignore *ign : ignores)
+ {
+ ListFormatter::ListEntry entry;
+ entry["Mask"] = ign->GetMask();
+ list.AddEntry(entry);
+ }
+
+ source.Reply(_("Memo ignore list:"));
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ }
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Allows you to ignore users from memoing you or a channel."
+ " If someone on the memo ignore list tries to memo you or a channel, they will not be told that you have them ignored."));
+ return true;
+ }
+};
+
+class MSIgnore : public Module
+{
+ CommandMSIgnore commandmsignore;
+
+ public:
+ MSIgnore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandmsignore(this)
+ {
+ }
+};
+
+MODULE_INIT(MSIgnore)
diff --git a/modules/memoserv/info.cpp b/modules/memoserv/info.cpp
new file mode 100644
index 000000000..d9bee1cb5
--- /dev/null
+++ b/modules/memoserv/info.cpp
@@ -0,0 +1,203 @@
+/* MemoServ core functions
+ *
+ * (C) 2003-2014 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 CommandMSInfo : public Command
+{
+ public:
+ CommandMSInfo(Module *creator) : Command(creator, "memoserv/info", 0, 1)
+ {
+ this->SetDesc(_("Displays information about your memos"));
+ this->SetSyntax(_("[\037user\037 | \037channel\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ NickServ::Account *nc = source.nc;
+ MemoServ::MemoInfo *mi;
+ NickServ::Nick *na = NULL;
+ ChanServ::Channel *ci = NULL;
+ const Anope::string &nname = !params.empty() ? params[0] : "";
+ bool hardmax;
+
+ if (!nname.empty() && nname[0] != '#' && source.HasPriv("memoserv/info"))
+ {
+ na = NickServ::FindNick(nname);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nname);
+ return;
+ }
+ mi = na->GetAccount()->GetMemos();
+ hardmax = na->GetAccount()->HasFieldS("MEMO_HARDMAX");
+ }
+ else if (!nname.empty() && nname[0] == '#')
+ {
+ ci = ChanServ::Find(nname);
+ if (!ci)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), nname);
+ return;
+ }
+
+ if (!source.AccessFor(ci).HasPriv("MEMO"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "MEMO", ci->GetName());
+ return;
+ }
+
+ mi = ci->GetMemos();
+ hardmax = ci->HasFieldS("MEMO_HARDMAX");
+ }
+ else if (!nname.empty()) /* It's not a chan and we aren't an oper */
+ {
+ source.Reply(_("Access denied. You do not have the correct operator privilege to see the memo info of \002{0}\002."), nname);
+ return;
+ }
+ else
+ {
+ mi = nc->GetMemos();
+ hardmax = nc->HasFieldS("MEMO_HARDMAX");
+ }
+ if (!mi)
+ return;
+
+ auto memos = mi->GetMemos();
+
+ if (!nname.empty() && (ci || na->GetAccount() != nc))
+ {
+ if (memos.empty())
+ source.Reply(_("%s currently has no memos."), nname.c_str());
+ else if (memos.size() == 1)
+ {
+ if (mi->GetMemo(0)->GetUnread())
+ source.Reply(_("\002{0}\002 currently has \0021\002 memo, and it has not yet been read."), nname);
+ else
+ source.Reply(_("\002{0}\002 currently has \0021\002 memo."), nname);
+ }
+ else
+ {
+ unsigned count = 0, i, end;
+ for (i = 0, end = memos.size(); i < end; ++i)
+ if (mi->GetMemo(i)->GetUnread())
+ ++count;
+ if (count == memos.size())
+ source.Reply(_("\002{0}\002 currently has \002{1]\002 memos; all of them are unread."), nname, count);
+ else if (!count)
+ source.Reply(_("\002{0}\002 currently has \002{1}\002 memos."), nname, memos.size());
+ else if (count == 1)
+ source.Reply(_("\002{0}\002 currently has \002{1}\002 memos, of which \0021\002 is unread."), nname, memos.size());
+ else
+ source.Reply(_("\002{0}\002 currently has \002{1]\002 memos, of which \002{2}\002 are unread."), nname, memos.size(), count);
+ }
+ if (mi->GetMemoMax() >= 0)
+ {
+ if (hardmax)
+ source.Reply(_("The memo limit of \002{0}\002 is \002{1}\002, and may not be changed."), nname, mi->GetMemoMax());
+ else
+ source.Reply(_("The memo limit of \002{0}\002 is \002{1}\002."), nname, mi->GetMemoMax());
+ }
+ else
+ source.Reply(_("\002{0}\002 has no memo limit."), nname);
+
+ if (na)
+ {
+ if (na->GetAccount()->HasFieldS("MEMO_RECEIVE") && na->GetAccount()->HasFieldS("MEMO_SIGNON"))
+ source.Reply(_("\002{0}\002 is notified of new memos at logon and when they arrive."), nname);
+ else if (na->GetAccount()->HasFieldS("MEMO_RECEIVE"))
+ source.Reply(_("\002{0}\002 is notified when new memos arrive."), nname);
+ else if (na->GetAccount()->HasFieldS("MEMO_SIGNON"))
+ source.Reply(_("\002{0}\002 is notified of news memos at logon."), nname);
+ else
+ source.Reply(_("\002{0}\002 is not notified of new memos."), nname);
+ }
+ }
+ else
+ {
+ if (memos.empty())
+ source.Reply(_("You currently have no memos."));
+ else if (memos.size() == 1)
+ {
+ if (mi->GetMemo(0)->GetUnread())
+ source.Reply(_("You currently have \0021\002 memo, and it has not yet been read."));
+ else
+ source.Reply(_("You currently have \0021\002 memo."));
+ }
+ else
+ {
+ unsigned count = 0, i, end;
+ for (i = 0, end = memos.size(); i < end; ++i)
+ if (mi->GetMemo(i)->GetUnread())
+ ++count;
+ if (count == memos.size())
+ source.Reply(_("You currently have \002{0}\002 memos; all of them are unread."), count);
+ else if (!count)
+ source.Reply(_("You currently have \002{0}\002 memos."), memos.size());
+ else if (count == 1)
+ source.Reply(_("You currently have \002{0}\002 memos, of which \0021\002 is unread."), memos.size());
+ else
+ source.Reply(_("You currently have \002{0}\002 memos, of which \002{1}\002 are unread."), memos.size(), count);
+ }
+
+ if (!mi->GetMemoMax())
+ {
+ if (!source.IsServicesOper() && hardmax)
+ source.Reply(_("Your memo limit is \0020\002; you will not receive any new memos. You cannot change this limit."));
+ else
+ source.Reply(_("Your memo limit is \0020\002; you will not receive any new memos."));
+ }
+ else if (mi->GetMemoMax() > 0)
+ {
+ if (!source.IsServicesOper() && hardmax)
+ source.Reply(_("Your memo limit is \002{0}\002, and may not be changed."), mi->GetMemoMax());
+ else
+ source.Reply(_("Your memo limit is \002{0}\002."), mi->GetMemoMax());
+ }
+ else
+ source.Reply(_("You have no limit on the number of memos you may keep."));
+
+ if (nc->HasFieldS("MEMO_RECEIVE") && nc->HasFieldS("MEMO_SIGNON"))
+ source.Reply(_("You will be notified of new memos at logon and when they arrive."));
+ else if (nc->HasFieldS("MEMO_RECEIVE"))
+ source.Reply(_("You will be notified when new memos arrive."));
+ else if (nc->HasFieldS("MEMO_SIGNON"))
+ source.Reply(_("You will be notified of new memos at logon."));
+ else
+ source.Reply(_("You will not be notified of new memos."));
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Without a parameter, displays information on the number of memos you have, how many of them are unread, and how many total memos you can receive."
+ " With a parameter, displays the same information for the given \037user\037 or \037channel\037, if you have the appropriate privilege.\n"
+ "\n"
+ "Use of this command on a channel requires the \002{0}\002 privilege on \037channel\037."),
+ source.command);
+
+ return true;
+ }
+};
+
+class MSInfo : public Module
+{
+ CommandMSInfo commandmsinfo;
+
+ public:
+ MSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandmsinfo(this)
+ {
+
+ }
+};
+
+MODULE_INIT(MSInfo)
diff --git a/modules/memoserv/list.cpp b/modules/memoserv/list.cpp
new file mode 100644
index 000000000..8215315b0
--- /dev/null
+++ b/modules/memoserv/list.cpp
@@ -0,0 +1,163 @@
+/* MemoServ core functions
+ *
+ * (C) 2003-2014 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 CommandMSList : public Command
+{
+ public:
+ CommandMSList(Module *creator) : Command(creator, "memoserv/list", 0, 2)
+ {
+ this->SetDesc(_("List your memos"));
+ this->SetSyntax(_("[\037channel\037] [\037list\037 | NEW]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+
+ Anope::string param = !params.empty() ? params[0] : "", chan;
+ ChanServ::Channel *ci = NULL;
+ MemoServ::MemoInfo *mi;
+
+ if (!param.empty() && param[0] == '#')
+ {
+ chan = param;
+ param = params.size() > 1 ? params[1] : "";
+
+ ci = ChanServ::Find(chan);
+ if (!ci)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ if (!source.AccessFor(ci).HasPriv("MEMO"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "MEMO", ci->GetName());
+ return;
+ }
+
+ mi = ci->GetMemos();
+ }
+ else
+ mi = source.nc->GetMemos();
+
+ if (!param.empty() && !isdigit(param[0]) && !param.equals_ci("NEW"))
+ {
+ this->OnSyntaxError(source, param);
+ return;
+ }
+
+ if (!mi)
+ return;
+
+ auto memos = mi->GetMemos();
+
+ if (!memos.size())
+ {
+ if (!chan.empty())
+ source.Reply(_("\002{0}\002 has no memos."), chan);
+ else
+ source.Reply(_("You have no memos."));
+ return;
+ }
+
+ ListFormatter list(source.GetAccount());
+
+ list.AddColumn(_("Number")).AddColumn(_("Sender")).AddColumn(_("Date/Time"));
+
+ if (!param.empty() && isdigit(param[0]))
+ {
+ NumberList(param, false,
+ [&](unsigned int number)
+ {
+ if (!number || number > memos.size())
+ return;
+
+ MemoServ::Memo *m = mi->GetMemo(number - 1);
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = (m->GetUnread() ? "* " : " ") + stringify(number);
+ entry["Sender"] = m->GetSender();
+ entry["Date/Time"] = Anope::strftime(m->GetTime(), source.GetAccount());
+ list.AddEntry(entry);
+ },
+ []{});
+ }
+ else
+ {
+ if (!param.empty())
+ {
+ unsigned i, end;
+ for (i = 0, end = memos.size(); i < end; ++i)
+ if (mi->GetMemo(i)->GetUnread())
+ break;
+ if (i == end)
+ {
+ if (!chan.empty())
+ source.Reply(_("\002{0}\002 has no new memos."), chan);
+ else
+ source.Reply(_("You have no new memos."));
+ return;
+ }
+ }
+
+ for (unsigned i = 0, end = memos.size(); i < end; ++i)
+ {
+ if (!param.empty() && !mi->GetMemo(i)->GetUnread())
+ continue;
+
+ MemoServ::Memo *m = mi->GetMemo(i);
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = (m->GetUnread() ? "* " : " ") + stringify(i + 1);
+ entry["Sender"] = m->GetSender();
+ entry["Date/Time"] = Anope::strftime(m->GetTime(), source.GetAccount());
+ list.AddEntry(entry);
+ }
+ }
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ source.Reply(_("Memos for \002{0}\002:"), ci ? ci->GetName().c_str() : source.GetNick().c_str());
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Lists the memos you currently have."
+ " With \002NEW\002, lists only new (unread) memos."
+ " Unread memos are marked with a \"*\" to the left of the memo number."
+ " You can also specify a list of numbers, as in the example below:\n"
+ "\n"
+ "Example:\n"
+ "\n"
+ " {0} 2-5,7-9\n"
+ " Lists memos numbered 2 through 5 and 7 through 9."));
+ return true;
+ }
+};
+
+class MSList : public Module
+{
+ CommandMSList commandmslist;
+
+ public:
+ MSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandmslist(this)
+ {
+
+ }
+};
+
+MODULE_INIT(MSList)
diff --git a/modules/memoserv/main/CMakeLists.txt b/modules/memoserv/main/CMakeLists.txt
new file mode 100644
index 000000000..781f0ef1f
--- /dev/null
+++ b/modules/memoserv/main/CMakeLists.txt
@@ -0,0 +1 @@
+build_subdir(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/memoserv/main/ignore.cpp b/modules/memoserv/main/ignore.cpp
new file mode 100644
index 000000000..e85011c00
--- /dev/null
+++ b/modules/memoserv/main/ignore.cpp
@@ -0,0 +1,27 @@
+#include "memo.h"
+#include "ignoretype.h"
+
+IgnoreImpl::~IgnoreImpl()
+{
+}
+
+MemoServ::MemoInfo *IgnoreImpl::GetMemoInfo()
+{
+ return Get(&IgnoreType::mi);
+}
+
+void IgnoreImpl::SetMemoInfo(MemoServ::MemoInfo *m)
+{
+ Set(&IgnoreType::mi, m);
+}
+
+Anope::string IgnoreImpl::GetMask()
+{
+ return Get(&IgnoreType::mask);
+}
+
+void IgnoreImpl::SetMask(const Anope::string &mask)
+{
+ Set(&IgnoreType::mask, mask);