From 1a3d9a016d3adc49788bbff73aac9b3b5ea85b17 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 1 Jul 2013 22:17:52 -0400 Subject: Change extensible keys to require explicitly having a type defined for it. Completely modularize more features like bs_kick, entrymsg, log, mode, etc. Move fantasy to its own module. Move greet to its own module. --- modules/commands/bs_assign.cpp | 65 ++- modules/commands/bs_badwords.cpp | 187 ++++++- modules/commands/bs_info.cpp | 127 +---- modules/commands/bs_kick.cpp | 623 ++++++++++++++++++----- modules/commands/bs_set.cpp | 317 +----------- modules/commands/cs_akick.cpp | 2 +- modules/commands/cs_clone.cpp | 19 +- modules/commands/cs_drop.cpp | 13 +- modules/commands/cs_enforce.cpp | 4 +- modules/commands/cs_entrymsg.cpp | 48 +- modules/commands/cs_info.cpp | 45 -- modules/commands/cs_list.cpp | 83 ++- modules/commands/cs_log.cpp | 126 ++++- modules/commands/cs_mode.cpp | 368 ++++++++++++- modules/commands/cs_seen.cpp | 2 +- modules/commands/cs_set.cpp | 263 ++++------ modules/commands/cs_set_misc.cpp | 57 ++- modules/commands/cs_suspend.cpp | 131 +++-- modules/commands/cs_topic.cpp | 129 ++++- modules/commands/greet.cpp | 210 ++++++++ modules/commands/hs_request.cpp | 60 +-- modules/commands/ms_info.cpp | 9 +- modules/commands/ms_set.cpp | 36 +- modules/commands/ns_ajoin.cpp | 53 +- modules/commands/ns_alist.cpp | 6 +- modules/commands/ns_cert.cpp | 224 ++++++-- modules/commands/ns_group.cpp | 10 +- modules/commands/ns_info.cpp | 170 +++++-- modules/commands/ns_list.cpp | 112 +++- modules/commands/ns_recover.cpp | 49 +- modules/commands/ns_register.cpp | 63 ++- modules/commands/ns_resetpass.cpp | 28 +- modules/commands/ns_set.cpp | 427 +++------------- modules/commands/ns_set_misc.cpp | 57 ++- modules/commands/ns_suspend.cpp | 125 +++-- modules/commands/os_defcon.cpp | 20 +- modules/commands/os_login.cpp | 21 +- modules/commands/os_noop.cpp | 20 +- modules/database/db_old.cpp | 175 ++++--- modules/database/db_plain.cpp | 339 +++--------- modules/extra/m_chanstats.cpp | 16 +- modules/extra/m_ldap_authentication.cpp | 15 +- modules/extra/webcpanel/pages/chanserv/set.cpp | 40 +- modules/extra/webcpanel/pages/index.cpp | 4 +- modules/extra/webcpanel/pages/logout.cpp | 4 +- modules/extra/webcpanel/pages/nickserv/alist.cpp | 4 +- modules/extra/webcpanel/pages/nickserv/cert.cpp | 7 +- modules/extra/webcpanel/pages/nickserv/info.cpp | 54 +- modules/extra/webcpanel/webcpanel.cpp | 3 +- modules/extra/webcpanel/webcpanel.h | 2 +- modules/fantasy.cpp | 200 ++++++++ modules/m_dnsbl.cpp | 4 +- modules/m_helpchan.cpp | 4 +- modules/protocol/charybdis.cpp | 19 +- modules/protocol/inspircd12.cpp | 5 +- modules/protocol/inspircd20.cpp | 18 +- modules/protocol/unreal.cpp | 24 +- modules/pseudoclients/botserv.cpp | 138 +---- modules/pseudoclients/chanserv.cpp | 104 +--- modules/pseudoclients/nickserv.cpp | 126 ++--- 60 files changed, 3207 insertions(+), 2407 deletions(-) create mode 100644 modules/commands/greet.cpp create mode 100644 modules/fantasy.cpp (limited to 'modules') diff --git a/modules/commands/bs_assign.cpp b/modules/commands/bs_assign.cpp index 7b241f09e..ba925ad80 100644 --- a/modules/commands/bs_assign.cpp +++ b/modules/commands/bs_assign.cpp @@ -145,14 +145,69 @@ class CommandBSUnassign : public Command } }; +class CommandBSSetNoBot : public Command +{ + public: + CommandBSSetNoBot(Module *creator, const Anope::string &sname = "botserv/set/nobot") : Command(creator, sname, 2, 2) + { + this->SetDesc(_("Prevent a bot from being assigned to a channel")); + this->SetSyntax(_("\037channel\037 {\037ON|OFF\037}")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) anope_override + { + ChannelInfo *ci = ChannelInfo::Find(params[0]); + const Anope::string &value = params[1]; + + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (value.equals_ci("ON")) + { + Log(LOG_ADMIN, source, this, ci) << "to enable nobot"; + + ci->Extend("BS_NOBOT"); + if (ci->bi) + ci->bi->UnAssign(source.GetUser(), ci); + source.Reply(_("No-bot mode is now \002on\002 on channel %s."), ci->name.c_str()); + } + else if (value.equals_ci("OFF")) + { + Log(LOG_ADMIN, source, this, ci) << "to disable nobot"; + + ci->Shrink("BS_NOBOT"); + source.Reply(_("No-bot mode is now \002off\002 on channel %s."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, source.command); + } + + bool OnHelp(CommandSource &source, const Anope::string &) anope_override + { + this->SendSyntax(source); + source.Reply(_(" \n" + "This option makes a channel be unassignable. If a bot\n" + "is already assigned to the channel, it is unassigned\n" + "automatically when you enable the option.")); + return true; + } +}; + class BSAssign : public Module { + ExtensibleItem nobot; + CommandBSAssign commandbsassign; CommandBSUnassign commandbsunassign; + CommandBSSetNoBot commandbssetnobot; public: BSAssign(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandbsassign(this), commandbsunassign(this) + nobot(this, "BS_NOBOT"), + commandbsassign(this), commandbsunassign(this), commandbssetnobot(this) { } @@ -163,7 +218,7 @@ class BSAssign : public Module return; AccessGroup access = c->ci->AccessFor(source); - if (c->ci->HasExt("BS_NOBOT") || (!access.HasPriv("ASSIGN") && !source->HasPriv("botserv/administration"))) + if (nobot.HasExt(c->ci) || (!access.HasPriv("ASSIGN") && !source->HasPriv("botserv/administration"))) { targ->SendMessage(bi, ACCESS_DENIED); return; @@ -184,6 +239,12 @@ class BSAssign : public Module bi->Assign(source, c->ci); targ->SendMessage(bi, _("Bot \002%s\002 has been assigned to %s."), bi->nick.c_str(), c->name.c_str()); } + + void OnBotInfo(CommandSource &source, BotInfo *bi, ChannelInfo *ci, InfoFormatter &info) anope_override + { + if (nobot.HasExt(ci)) + info.AddOption(_("No bot")); + } }; MODULE_INIT(BSAssign) diff --git a/modules/commands/bs_badwords.cpp b/modules/commands/bs_badwords.cpp index 22fa74714..ff9707778 100644 --- a/modules/commands/bs_badwords.cpp +++ b/modules/commands/bs_badwords.cpp @@ -10,11 +10,132 @@ */ #include "module.h" +#include "modules/bs_badwords.h" + +struct BadWordImpl : BadWord, Serializable +{ + BadWordImpl() : Serializable("BadWord") { } + ~BadWordImpl(); + + void Serialize(Serialize::Data &data) const anope_override + { + data["ci"] << this->chan; + data["word"] << this->word; + data.SetType("type", Serialize::Data::DT_INT); data["type"] << this->type; + } + + static Serializable* Unserialize(Serializable *obj, Serialize::Data &); +}; + +struct BadWordsImpl : BadWords +{ + Serialize::Reference ci; + typedef std::vector list; + Serialize::Checker badwords; + + BadWordsImpl(Extensible *obj) : ci(anope_dynamic_static_cast(obj)), badwords("BadWord") { } + + BadWord* AddBadWord(const Anope::string &word, BadWordType type) anope_override + { + BadWordImpl *bw = new BadWordImpl(); + bw->chan = ci->name; + bw->word = word; + bw->type = type; + + this->badwords->push_back(bw); + + FOREACH_MOD(OnBadWordAdd, (ci, bw)); + + return bw; + } + + BadWord* GetBadWord(unsigned index) const anope_override + { + if (this->badwords->empty() || index >= this->badwords->size()) + return NULL; + + BadWordImpl *bw = (*this->badwords)[index]; + bw->QueueUpdate(); + return bw; + } + + unsigned GetBadWordCount() const anope_override + { + return this->badwords->size(); + } + + void EraseBadWord(unsigned index) anope_override + { + if (this->badwords->empty() || index >= this->badwords->size()) + return; + + FOREACH_MOD(OnBadWordDel, (ci, (*this->badwords)[index])); + + delete this->badwords->at(index); + } + + void ClearBadWords() anope_override + { + while (!this->badwords->empty()) + delete this->badwords->back(); + } + + void Check() anope_override + { + if (this->badwords->empty()) + ci->Shrink("badwords"); + } +}; + +BadWordImpl::~BadWordImpl() +{ + ChannelInfo *ci = ChannelInfo::Find(chan); + if (ci) + { + BadWordsImpl *badwords = ci->GetExt("badwords"); + if (badwords) + { + BadWordsImpl::list::iterator it = std::find(badwords->badwords->begin(), badwords->badwords->end(), this); + if (it != badwords->badwords->end()) + badwords->badwords->erase(it); + } + } +} + +Serializable* BadWordImpl::Unserialize(Serializable *obj, Serialize::Data &data) +{ + Anope::string sci, sword; + + data["ci"] >> sci; + data["word"] >> sword; + + ChannelInfo *ci = ChannelInfo::Find(sci); + if (!ci) + return NULL; + + unsigned int n; + data["type"] >> n; + + BadWordImpl *bw; + if (obj) + bw = anope_dynamic_static_cast(obj); + else + bw = new BadWordImpl(); + bw->chan = sci; + bw->word = sword; + bw->type = static_cast(n); + + BadWordsImpl *bws = ci->Require("badwords"); + bws->badwords->push_back(bw); + + return bw; +} class BadwordsDelCallback : public NumberList { CommandSource &source; ChannelInfo *ci; + BadWords *bw; Command *c; unsigned deleted; bool override; @@ -23,6 +144,7 @@ class BadwordsDelCallback : public NumberList { if (!source.AccessFor(ci).HasPriv("BADWORDS") && source.HasPriv("botserv/administration")) this->override = true; + bw = ci->Require("badwords"); } ~BadwordsDelCallback() @@ -37,12 +159,12 @@ class BadwordsDelCallback : public NumberList void HandleNumber(unsigned Number) anope_override { - if (!Number || Number > ci->GetBadWordCount()) + if (!bw || !Number || Number > bw->GetBadWordCount()) return; - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, c, ci) << "DEL " << ci->GetBadWord(Number - 1)->word; + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, c, ci) << "DEL " << bw->GetBadWord(Number - 1)->word; ++deleted; - ci->EraseBadWord(Number - 1); + bw->EraseBadWord(Number - 1); } }; @@ -54,10 +176,11 @@ class CommandBSBadwords : public Command bool override = !source.AccessFor(ci).HasPriv("BADWORDS"); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "LIST"; ListFormatter list; + BadWords *bw = ci->GetExt("badwords"); list.AddColumn("Number").AddColumn("Word").AddColumn("Type"); - if (!ci->GetBadWordCount()) + if (!bw || !bw->GetBadWordCount()) { source.Reply(_("%s bad words list is empty."), ci->name.c_str()); return; @@ -68,40 +191,41 @@ class CommandBSBadwords : public Command { ListFormatter &list; ChannelInfo *ci; + BadWords *bw; public: - BadwordsListCallback(ListFormatter &_list, ChannelInfo *_ci, const Anope::string &numlist) : NumberList(numlist, false), list(_list), ci(_ci) + BadwordsListCallback(ListFormatter &_list, ChannelInfo *_ci, BadWords *_bw, const Anope::string &numlist) : NumberList(numlist, false), list(_list), ci(_ci), bw(_bw) { } void HandleNumber(unsigned Number) anope_override { - if (!Number || Number > ci->GetBadWordCount()) + if (!Number || Number > bw->GetBadWordCount()) return; - const BadWord *bw = ci->GetBadWord(Number - 1); + const BadWord *b = bw->GetBadWord(Number - 1); ListFormatter::ListEntry entry; entry["Number"] = stringify(Number); - entry["Word"] = bw->word; - entry["Type"] = bw->type == BW_SINGLE ? "(SINGLE)" : (bw->type == BW_START ? "(START)" : (bw->type == BW_END ? "(END)" : "")); + entry["Word"] = b->word; + entry["Type"] = b->type == BW_SINGLE ? "(SINGLE)" : (b->type == BW_START ? "(START)" : (b->type == BW_END ? "(END)" : "")); this->list.AddEntry(entry); } } - nl_list(list, ci, word); + nl_list(list, ci, bw, word); nl_list.Process(); } else { - for (unsigned i = 0, end = ci->GetBadWordCount(); i < end; ++i) + for (unsigned i = 0, end = bw->GetBadWordCount(); i < end; ++i) { - const BadWord *bw = ci->GetBadWord(i); + const BadWord *b = bw->GetBadWord(i); - if (!word.empty() && !Anope::Match(bw->word, word)) + if (!word.empty() && !Anope::Match(b->word, word)) continue; ListFormatter::ListEntry entry; entry["Number"] = stringify(i + 1); - entry["Word"] = bw->word; - entry["Type"] = bw->type == BW_SINGLE ? "(SINGLE)" : (bw->type == BW_START ? "(START)" : (bw->type == BW_END ? "(END)" : "")); + entry["Word"] = b->word; + entry["Type"] = b->type == BW_SINGLE ? "(SINGLE)" : (b->type == BW_START ? "(START)" : (b->type == BW_END ? "(END)" : "")); list.AddEntry(entry); } } @@ -127,6 +251,7 @@ class CommandBSBadwords : public Command size_t pos = word.rfind(' '); BadWordType bwtype = BW_ANY; Anope::string realword = word; + BadWords *badwords = ci->Require("badwords"); if (pos != Anope::string::npos) { @@ -144,7 +269,7 @@ class CommandBSBadwords : public Command } unsigned badwordsmax = Config->GetModule(this->module)->Get("badwordsmax"); - if (ci->GetBadWordCount() >= badwordsmax) + if (badwords->GetBadWordCount() >= badwordsmax) { source.Reply(_("Sorry, you can only have %d bad words entries on a channel."), badwordsmax); return; @@ -152,9 +277,9 @@ class CommandBSBadwords : public Command bool casesensitive = Config->GetModule("botserv")->Get("casesensitive"); - for (unsigned i = 0, end = ci->GetBadWordCount(); i < end; ++i) + for (unsigned i = 0, end = badwords->GetBadWordCount(); i < end; ++i) { - const BadWord *bw = ci->GetBadWord(i); + const BadWord *bw = badwords->GetBadWord(i); if ((casesensitive && realword.equals_cs(bw->word)) || (!casesensitive && realword.equals_ci(bw->word))) { @@ -165,13 +290,21 @@ class CommandBSBadwords : public Command bool override = !source.AccessFor(ci).HasPriv("BADWORDS"); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "ADD " << realword; - ci->AddBadWord(realword, bwtype); + badwords->AddBadWord(realword, bwtype); source.Reply(_("\002%s\002 added to %s bad words list."), realword.c_str(), ci->name.c_str()); } void DoDelete(CommandSource &source, ChannelInfo *ci, const Anope::string &word) { + BadWords *badwords = ci->GetExt("badwords"); + + if (!badwords || !badwords->GetBadWordCount()) + { + source.Reply(_("%s bad words list is empty."), ci->name.c_str()); + return; + } + /* Special case: is it a number/list? Only do search if it isn't. */ if (!word.empty() && isdigit(word[0]) && word.find_first_not_of("1234567890,-") == Anope::string::npos) { @@ -183,9 +316,9 @@ class CommandBSBadwords : public Command unsigned i, end; const BadWord *badword; - for (i = 0, end = ci->GetBadWordCount(); i < end; ++i) + for (i = 0, end = badwords->GetBadWordCount(); i < end; ++i) { - badword = ci->GetBadWord(i); + badword = badwords->GetBadWord(i); if (word.equals_ci(badword->word)) break; @@ -202,10 +335,10 @@ class CommandBSBadwords : public Command source.Reply(_("\002%s\002 deleted from %s bad words list."), badword->word.c_str(), ci->name.c_str()); - ci->EraseBadWord(i); + badwords->EraseBadWord(i); } - return; + badwords->Check(); } void DoClear(CommandSource &source, ChannelInfo *ci) @@ -213,7 +346,9 @@ class CommandBSBadwords : public Command bool override = !source.AccessFor(ci).HasPriv("BADWORDS"); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "CLEAR"; - ci->ClearBadWords(); + BadWords *badwords = ci->GetExt("badwords"); + if (badwords) + badwords->ClearBadWords(); source.Reply(_("Bad words list is now empty.")); } @@ -309,10 +444,12 @@ class CommandBSBadwords : public Command class BSBadwords : public Module { CommandBSBadwords commandbsbadwords; + ExtensibleItem badwords; + Serialize::Type badword_type; public: BSBadwords(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandbsbadwords(this) + commandbsbadwords(this), badwords(this, "badwords"), badword_type("BadWord", BadWordImpl::Unserialize) { } }; diff --git a/modules/commands/bs_info.cpp b/modules/commands/bs_info.cpp index 8bbfd0b18..a3eb9ce2d 100644 --- a/modules/commands/bs_info.cpp +++ b/modules/commands/bs_info.cpp @@ -36,16 +36,6 @@ class CommandBSInfo : public Command buffers.push_back(buf); } - void CheckOptStr(Anope::string &buf, const Anope::string &flag, const char *option, Extensible *flags, const NickCore *nc) - { - if (flags->HasExt(flag)) - { - if (!buf.empty()) - buf += ", "; - buf += Language::Translate(nc, option); - } - } - public: CommandBSInfo(Module *creator) : Command(creator, "botserv/info", 1, 1) { @@ -57,8 +47,8 @@ class CommandBSInfo : public Command { const Anope::string &query = params[0]; - const BotInfo *bi = BotInfo::Find(query, true); - ChannelInfo *ci; + BotInfo *bi = BotInfo::Find(query, true); + ChannelInfo *ci = ChannelInfo::Find(query); InfoFormatter info(source.nc); if (bi) @@ -70,6 +60,8 @@ class CommandBSInfo : public Command info[_("Options")] = bi->oper_only ? _("Private") : _("None"); info[_("Used on")] = stringify(bi->GetChannelCount()) + " channel(s)"; + FOREACH_MOD(OnBotInfo, (source, bi, ci, info)); + std::vector replies; info.Process(replies); @@ -85,7 +77,7 @@ class CommandBSInfo : public Command } } - else if ((ci = ChannelInfo::Find(query))) + else if (ci) { if (!source.AccessFor(ci).HasPriv("INFO") && !source.HasPriv("botserv/administration")) { @@ -99,114 +91,7 @@ class CommandBSInfo : public Command Anope::string enabled = Language::Translate(source.nc, _("Enabled")); Anope::string disabled = Language::Translate(source.nc, _("Disabled")); - if (ci->HasExt("BS_KICK_BADWORDS")) - { - if (ci->ttb[TTB_BADWORDS]) - info[_("Bad words kicker")] = Anope::printf("%s (%d kick(s) to ban)", enabled.c_str(), ci->ttb[TTB_BADWORDS]); - else - info[_("Bad words kicker")] = enabled; - } - else - info[_("Bad words kicker")] = disabled; - - if (ci->HasExt("BS_KICK_BOLDS")) - { - if (ci->ttb[TTB_BOLDS]) - info[_("Bolds kicker")] = Anope::printf("%s (%d kick(s) to ban)", enabled.c_str(), ci->ttb[TTB_BOLDS]); - else - info[_("Bolds kicker")] = enabled; - } - else - info[_("Bolds kicker")] = disabled; - - if (ci->HasExt("BS_KICK_CAPS")) - { - if (ci->ttb[TTB_CAPS]) - info[_("Caps kicker")] = Anope::printf(_("%s (%d kick(s) to ban; minimum %d/%d%%"), enabled.c_str(), ci->ttb[TTB_CAPS], ci->capsmin, ci->capspercent); - else - info[_("Caps kicker")] = Anope::printf(_("%s (minimum %d/%d%%)"), enabled.c_str(), ci->capsmin, ci->capspercent); - } - else - info[_("Caps kicker")] = disabled; - - if (ci->HasExt("BS_KICK_COLORS")) - { - if (ci->ttb[TTB_COLORS]) - info[_("Colors kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), ci->ttb[TTB_COLORS]); - else - info[_("Colors kicker")] = enabled; - } - else - info[_("Colors kicker")] = disabled; - - if (ci->HasExt("BS_KICK_FLOOD")) - { - if (ci->ttb[TTB_FLOOD]) - info[_("Flood kicker")] = Anope::printf(_("%s (%d kick(s) to ban; %d lines in %ds"), enabled.c_str(), ci->ttb[TTB_FLOOD], ci->floodlines, ci->floodsecs); - else - info[_("Flood kicker")] = Anope::printf(_("%s (%d lines in %ds)"), enabled.c_str(), ci->floodlines, ci->floodsecs); - } - else - info[_("Flood kicker")] = disabled; - - if (ci->HasExt("BS_KICK_REPEAT")) - { - if (ci->ttb[TTB_REPEAT]) - info[_("Repeat kicker")] = Anope::printf(_("%s (%d kick(s) to ban; %d times)"), enabled.c_str(), ci->ttb[TTB_REPEAT], ci->repeattimes); - else - info[_("Repeat kicker")] = Anope::printf(_("%s (%d times)"), enabled.c_str(), ci->repeattimes); - } - else - info[_("Repeat kicker")] = disabled; - - if (ci->HasExt("BS_KICK_REVERSES")) - { - if (ci->ttb[TTB_REVERSES]) - info[_("Reverses kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), ci->ttb[TTB_REVERSES]); - else - info[_("Reverses kicker")] = enabled; - } - else - info[_("Reverses kicker")] = disabled; - - if (ci->HasExt("BS_KICK_UNDERLINES")) - { - if (ci->ttb[TTB_UNDERLINES]) - info[_("Underlines kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), ci->ttb[TTB_UNDERLINES]); - else - info[_("Underlines kicker")] = enabled; - } - else - info[_("Underlines kicker")] = disabled; - - if (ci->HasExt("BS_KICK_ITALICS")) - { - if (ci->ttb[TTB_ITALICS]) - info[_("Italics kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), ci->ttb[TTB_ITALICS]); - else - info[_("Italics kicker")] = enabled; - } - else - info[_("Italics kicker")] = disabled; - - if (ci->HasExt("BS_KICK_AMSGS")) - { - if (ci->ttb[TTB_AMSGS]) - info[_("AMSG kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), ci->ttb[TTB_AMSGS]); - else - info[_("AMSG kicker")] = enabled; - } - else - info[_("AMSG kicker")] = disabled; - - Anope::string flags; - CheckOptStr(flags, "BS_DONTKICKOPS", _("Ops protection"), ci, source.nc); - CheckOptStr(flags, "BS_DONTKICKVOICES", _("Voices protection"), ci, source.nc); - CheckOptStr(flags, "BS_FANTASY", _("Fantasy"), ci, source.nc); - CheckOptStr(flags, "BS_GREET", _("Greet"), ci, source.nc); - CheckOptStr(flags, "BS_NOBOT", _("No bot"), ci, source.nc); - - info[_("Options")] = flags.empty() ? _("None") : flags; + FOREACH_MOD(OnBotInfo, (source, bi, ci, info)); std::vector replies; info.Process(replies); diff --git a/modules/commands/bs_kick.cpp b/modules/commands/bs_kick.cpp index 50d4a0e5f..b22dd9ccb 100644 --- a/modules/commands/bs_kick.cpp +++ b/modules/commands/bs_kick.cpp @@ -12,9 +12,107 @@ */ #include "module.h" +#include "modules/bs_kick.h" +#include "modules/bs_badwords.h" static Module *me; +struct KickerDataImpl : KickerData +{ + KickerDataImpl(Extensible *obj) + { + amsgs = badwords = bolds = caps = colors = flood = italics = repeat = reverses = underlines = false; + for (int16_t i = 0; i < TTB_SIZE; ++i) + ttb[i] = 0; + capsmin = capspercent = 0; + floodlines = floodsecs = 0; + repeattimes = 0; + + dontkickops = dontkickvoices = false; + } + + void Check(ChannelInfo *ci) anope_override + { + if (amsgs || badwords || bolds || caps || colors || flood || italics || repeat || reverses || underlines) + return; + + ci->Shrink("kickerdata"); + } + + struct ExtensibleItem : ::ExtensibleItem + { + ExtensibleItem(Module *m, const Anope::string &name) : ::ExtensibleItem(m, name) { } + + void ExtensibleSerialize(const Extensible *e, const Serializable *s, Serialize::Data &data) const anope_override + { + if (s->GetSerializableType()->GetName() != "ChannelInfo") + return; + + const ChannelInfo *ci = anope_dynamic_static_cast(e); + KickerData *kd = this->Get(ci); + if (kd == NULL) + return; + + data["kickerdata:amsgs"] << kd->amsgs; + data["kickerdata:badwords"] << kd->badwords; + data["kickerdata:bolds"] << kd->bolds; + data["kickerdata:caps"] << kd->caps; + data["kickerdata:colors"] << kd->colors; + data["kickerdata:flood"] << kd->flood; + data["kickerdata:italics"] << kd->italics; + data["kickerdata:repeat"] << kd->repeat; + data["kickerdata:reverses"] << kd->reverses; + data["kickerdata:underlines"] << kd->underlines; + + data.SetType("capsmin", Serialize::Data::DT_INT); data["capsmin"] << kd->capsmin; + data.SetType("capspercent", Serialize::Data::DT_INT); data["capspercent"] << kd->capspercent; + data.SetType("floodlines", Serialize::Data::DT_INT); data["floodlines"] << kd->floodlines; + data.SetType("floodsecs", Serialize::Data::DT_INT); data["floodsecs"] << kd->floodsecs; + data.SetType("repeattimes", Serialize::Data::DT_INT); data["repeattimes"] << kd->repeattimes; + for (int16_t i = 0; i < TTB_SIZE; ++i) + data["ttb"] << kd->ttb[i] << " "; + } + + void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) anope_override + { + if (s->GetSerializableType()->GetName() != "ChannelInfo") + return; + + ChannelInfo *ci = anope_dynamic_static_cast(e); + KickerData *kd = ci->Require("kickerdata"); + + data["kickerdata:amsgs"] >> kd->amsgs; + data["kickerdata:badwords"] >> kd->badwords; + data["kickerdata:bolds"] >> kd->bolds; + data["kickerdata:caps"] >> kd->caps; + data["kickerdata:colors"] >> kd->colors; + data["kickerdata:flood"] >> kd->flood; + data["kickerdata:italics"] >> kd->italics; + data["kickerdata:repeat"] >> kd->repeat; + data["kickerdata:reverses"] >> kd->reverses; + data["kickerdata:underlines"] >> kd->underlines; + + data["capsmin"] >> kd->capsmin; + data["capspercent"] >> kd->capspercent; + data["floodlines"] >> kd->floodlines; + data["floodsecs"] >> kd->floodsecs; + data["repeattimes"] >> kd->repeattimes; + + Anope::string ttb, tok; + data["ttb"] >> ttb; + spacesepstream sep(ttb); + for (int i = 0; sep.GetToken(tok) && i < TTB_SIZE; ++i) + try + { + ttb[i] = convertTo(tok); + } + catch (const ConvertException &) { } + + kd->Check(ci); + } + }; +}; + class CommandBSKick : public Command { public: @@ -99,7 +197,7 @@ class CommandBSKickBase : public Command return false; } - void Process(CommandSource &source, ChannelInfo *ci, const Anope::string ¶m, const Anope::string &ttb, size_t ttb_idx, const Anope::string &optname, const Anope::string &mdname) + void Process(CommandSource &source, ChannelInfo *ci, const Anope::string ¶m, const Anope::string &ttb, size_t ttb_idx, const Anope::string &optname, KickerData *kd, bool &val) { if (param.equals_ci("ON")) { @@ -119,15 +217,15 @@ class CommandBSKickBase : public Command return; } - ci->ttb[ttb_idx] = i; + kd->ttb[ttb_idx] = i; } else - ci->ttb[ttb_idx] = 0; + kd->ttb[ttb_idx] = 0; - ci->ExtendMetadata(mdname); - if (ci->ttb[ttb_idx]) + val = true; + if (kd->ttb[ttb_idx]) source.Reply(_("Bot will now kick for \002%s\002, and will place a ban\n" - "after %d kicks for the same user."), optname.c_str(), ci->ttb[ttb_idx]); + "after %d kicks for the same user."), optname.c_str(), kd->ttb[ttb_idx]); else source.Reply(_("Bot will now kick for \002%s\002."), optname.c_str()); @@ -139,7 +237,7 @@ class CommandBSKickBase : public Command bool override = !source.AccessFor(ci).HasPriv("SET"); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable the " << optname << "kicker"; - ci->Shrink(mdname); + val = false; source.Reply(_("Bot won't kick for \002%s\002 anymore."), optname.c_str()); } else @@ -160,7 +258,11 @@ class CommandBSKickAMSG : public CommandBSKickBase { ChannelInfo *ci; if (CheckArguments(source, params, ci)) - Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_AMSGS, "AMSG", "BS_KICK_AMSGS"); + { + KickerData *kd = ci->Require("kickerdata"); + Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_AMSGS, "AMSG", kd, kd->amsgs); + kd->Check(ci); + } } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -188,7 +290,12 @@ class CommandBSKickBadwords : public CommandBSKickBase { ChannelInfo *ci; if (CheckArguments(source, params, ci)) - Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_BADWORDS, "badwords", "BS_KICK_BADWORDS"); + { + KickerData *kd = ci->Require("kickerdata"); + Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_BADWORDS, "badwords", kd, kd->badwords); + kd->Check(ci); + } + } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -219,7 +326,11 @@ class CommandBSKickBolds : public CommandBSKickBase { ChannelInfo *ci; if (CheckArguments(source, params, ci)) - Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_BOLDS, "bolds", "BS_KICK_BOLDS"); + { + KickerData *kd = ci->Require("kickerdata"); + Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_BOLDS, "bolds", kd, kd->bolds); + kd->Check(ci); + } } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -248,6 +359,8 @@ class CommandBSKickCaps : public CommandBSKickBase if (!CheckArguments(source, params, ci)) return; + KickerData *kd = ci->Require("kickerdata"); + if (params[1].equals_ci("ON")) { const Anope::string &ttb = params.size() > 2 ? params[2] : "", @@ -258,52 +371,54 @@ class CommandBSKickCaps : public CommandBSKickBase { try { - ci->ttb[TTB_CAPS] = convertTo(ttb); - if (ci->ttb[TTB_CAPS] < 0) + kd->ttb[TTB_CAPS] = convertTo(ttb); + if (kd->ttb[TTB_CAPS] < 0) throw ConvertException(); } catch (const ConvertException &) { - ci->ttb[TTB_CAPS] = 0; + kd->ttb[TTB_CAPS] = 0; source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); return; } } else - ci->ttb[TTB_CAPS] = 0; + kd->ttb[TTB_CAPS] = 0; - ci->capsmin = 10; + kd->capsmin = 10; try { - ci->capsmin = convertTo(min); + kd->capsmin = convertTo(min); } catch (const ConvertException &) { } - if (ci->capsmin < 1) - ci->capsmin = 10; + if (kd->capsmin < 1) + kd->capsmin = 10; - ci->capspercent = 25; + kd->capspercent = 25; try { - ci->capspercent = convertTo(percent); + kd->capspercent = convertTo(percent); } catch (const ConvertException &) { } - if (ci->capspercent < 1 || ci->capspercent > 100) - ci->capspercent = 25; + if (kd->capspercent < 1 || kd->capspercent > 100) + kd->capspercent = 25; - ci->ExtendMetadata("BS_KICK_CAPS"); - if (ci->ttb[TTB_CAPS]) + kd->caps = true; + if (kd->ttb[TTB_CAPS]) source.Reply(_("Bot will now kick for \002caps\002 (they must constitute at least\n" "%d characters and %d%% of the entire message), and will\n" - "place a ban after %d kicks for the same user."), ci->capsmin, ci->capspercent, ci->ttb[TTB_CAPS]); + "place a ban after %d kicks for the same user."), kd->capsmin, kd->capspercent, kd->ttb[TTB_CAPS]); else source.Reply(_("Bot will now kick for \002caps\002 (they must constitute at least\n" - "%d characters and %d%% of the entire message)."), ci->capsmin, ci->capspercent); + "%d characters and %d%% of the entire message)."), kd->capsmin, kd->capspercent); } else { - ci->Shrink("BS_KICK_CAPS"); + kd->caps = false; source.Reply(_("Bot won't kick for \002caps\002 anymore.")); } + + kd->Check(ci); } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -335,7 +450,11 @@ class CommandBSKickColors : public CommandBSKickBase { ChannelInfo *ci; if (CheckArguments(source, params, ci)) - Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_COLORS, "colors", "BS_KICK_COLORS"); + { + KickerData *kd = ci->Require("kickerdata"); + Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_COLORS, "colors", kd, kd->colors); + kd->Check(ci); + } } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -364,6 +483,8 @@ class CommandBSKickFlood : public CommandBSKickBase if (!CheckArguments(source, params, ci)) return; + KickerData *kd = ci->Require("kickerdata"); + if (params[1].equals_ci("ON")) { const Anope::string &ttb = params.size() > 2 ? params[2] : "", @@ -386,45 +507,47 @@ class CommandBSKickFlood : public CommandBSKickBase return; } - ci->ttb[TTB_FLOOD] = i; + kd->ttb[TTB_FLOOD] = i; } else - ci->ttb[TTB_FLOOD] = 0; + kd->ttb[TTB_FLOOD] = 0; - ci->floodlines = 6; + kd->floodlines = 6; try { - ci->floodlines = convertTo(lines); + kd->floodlines = convertTo(lines); } catch (const ConvertException &) { } - if (ci->floodlines < 2) - ci->floodlines = 6; + if (kd->floodlines < 2) + kd->floodlines = 6; - ci->floodsecs = 10; + kd->floodsecs = 10; try { - ci->floodsecs = convertTo(secs); + kd->floodsecs = convertTo(secs); } catch (const ConvertException &) { } - if (ci->floodsecs < 1) - ci->floodsecs = 10; - if (ci->floodsecs > Config->GetModule(me)->Get("keepdata")) - ci->floodsecs = Config->GetModule(me)->Get("keepdata"); + if (kd->floodsecs < 1) + kd->floodsecs = 10; + if (kd->floodsecs > Config->GetModule(me)->Get("keepdata")) + kd->floodsecs = Config->GetModule(me)->Get("keepdata"); - ci->ExtendMetadata("BS_KICK_FLOOD"); - if (ci->ttb[TTB_FLOOD]) + kd->flood = true; + if (kd->ttb[TTB_FLOOD]) source.Reply(_("Bot will now kick for \002flood\002 (%d lines in %d seconds\n" - "and will place a ban after %d kicks for the same user."), ci->floodlines, ci->floodsecs, ci->ttb[TTB_FLOOD]); + "and will place a ban after %d kicks for the same user."), kd->floodlines, kd->floodsecs, kd->ttb[TTB_FLOOD]); else - source.Reply(_("Bot will now kick for \002flood\002 (%d lines in %d seconds)."), ci->floodlines, ci->floodsecs); + source.Reply(_("Bot will now kick for \002flood\002 (%d lines in %d seconds)."), kd->floodlines, kd->floodsecs); } else if (params[1].equals_ci("OFF")) { - ci->Shrink("BS_KICK_FLOOD"); + kd->flood = false; source.Reply(_("Bot won't kick for \002flood\002 anymore.")); } else this->OnSyntaxError(source, params[1]); + + kd->Check(ci); } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -454,7 +577,11 @@ class CommandBSKickItalics : public CommandBSKickBase { ChannelInfo *ci; if (CheckArguments(source, params, ci)) - Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_ITALICS, "italics", "BS_KICK_ITALICS"); + { + KickerData *kd = ci->Require("kickerdata"); + Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_ITALICS, "italics", kd, kd->italics); + kd->Check(ci); + } } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -483,6 +610,8 @@ class CommandBSKickRepeat : public CommandBSKickBase if (!CheckArguments(source, params, ci)) return; + KickerData *kd = ci->Require("kickerdata"); + if (params[1].equals_ci("ON")) { const Anope::string &ttb = params[2], @@ -504,36 +633,38 @@ class CommandBSKickRepeat : public CommandBSKickBase return; } - ci->ttb[TTB_REPEAT] = i; + kd->ttb[TTB_REPEAT] = i; } else - ci->ttb[TTB_REPEAT] = 0; + kd->ttb[TTB_REPEAT] = 0; - ci->repeattimes = 3; + kd->repeattimes = 3; try { - ci->repeattimes = convertTo(times); + kd->repeattimes = convertTo(times); } catch (const ConvertException &) { } - if (ci->repeattimes < 2) - ci->repeattimes = 3; + if (kd->repeattimes < 2) + kd->repeattimes = 3; - ci->ExtendMetadata("BS_KICK_REPEAT"); - if (ci->ttb[TTB_REPEAT]) + kd->repeat = true; + if (kd->ttb[TTB_REPEAT]) source.Reply(_("Bot will now kick for \002repeats\002 (users that say the\n" "same thing %d times), and will place a ban after %d\n" - "kicks for the same user."), ci->repeattimes, ci->ttb[TTB_REPEAT]); + "kicks for the same user."), kd->repeattimes, kd->ttb[TTB_REPEAT]); else source.Reply(_("Bot will now kick for \002repeats\002 (users that say the\n" - "same thing %d times)."), ci->repeattimes); + "same thing %d times)."), kd->repeattimes); } else if (params[1].equals_ci("OFF")) { - ci->Shrink("BS_KICK_REPEAT"); + kd->repeat = false; source.Reply(_("Bot won't kick for \002repeats\002 anymore.")); } else this->OnSyntaxError(source, params[1]); + + kd->Check(ci); } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -562,7 +693,11 @@ class CommandBSKickReverses : public CommandBSKickBase { ChannelInfo *ci; if (CheckArguments(source, params, ci)) - Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_ITALICS, "italics", "BS_KICK_ITALICS"); + { + KickerData *kd = ci->Require("kickerdata"); + Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_REVERSES, "reverses", kd, kd->reverses); + kd->Check(ci); + } } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -589,7 +724,11 @@ class CommandBSKickUnderlines : public CommandBSKickBase { ChannelInfo *ci; if (CheckArguments(source, params, ci)) - Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_ITALICS, "italics", "BS_KICK_ITALICS"); + { + KickerData *kd = ci->Require("kickerdata"); + Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_UNDERLINES, "underlines", kd, kd->underlines); + kd->Check(ci); + } } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -603,7 +742,137 @@ class CommandBSKickUnderlines : public CommandBSKickBase } }; -struct BanData : ExtensibleItem +class CommandBSSetDontKickOps : public Command +{ + public: + CommandBSSetDontKickOps(Module *creator, const Anope::string &sname = "botserv/set/dontkickops") : Command(creator, sname, 2, 2) + { + this->SetDesc(_("To protect ops against bot kicks")); + this->SetSyntax(_("\037channel\037 {ON | OFF}")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) anope_override + { + ChannelInfo *ci = ChannelInfo::Find(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + AccessGroup access = source.AccessFor(ci); + if (!source.HasPriv("botserv/administration") && !access.HasPriv("SET")) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (Anope::ReadOnly) + { + source.Reply(_("Sorry, bot option setting is temporarily disabled.")); + return; + } + + KickerData *kd = ci->Require("kickerdata"); + if (params[1].equals_ci("ON")) + { + bool override = !access.HasPriv("SET"); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable dontkickops"; + + kd->dontkickops = true; + source.Reply(_("Bot \002won't kick ops\002 on channel %s."), ci->name.c_str()); + } + else if (params[1].equals_ci("OFF")) + { + bool override = !access.HasPriv("SET"); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable dontkickops"; + + kd->dontkickops = false; + source.Reply(_("Bot \002will kick ops\002 on channel %s."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, source.command); + + kd->Check(ci); + } + + bool OnHelp(CommandSource &source, const Anope::string &) anope_override + { + this->SendSyntax(source); + source.Reply(_(" \n" + "Enables or disables \002ops protection\002 mode on a channel.\n" + "When it is enabled, ops won't be kicked by the bot\n" + "even if they don't match the NOKICK level.")); + return true; + } +}; + +class CommandBSSetDontKickVoices : public Command +{ + public: + CommandBSSetDontKickVoices(Module *creator, const Anope::string &sname = "botserv/set/dontkickvoices") : Command(creator, sname, 2, 2) + { + this->SetDesc(_("To protect voices against bot kicks")); + this->SetSyntax(_("\037channel\037 {ON | OFF}")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) anope_override + { + ChannelInfo *ci = ChannelInfo::Find(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + AccessGroup access = source.AccessFor(ci); + if (!source.HasPriv("botserv/administration") && !access.HasPriv("SET")) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (Anope::ReadOnly) + { + source.Reply(_("Sorry, bot option setting is temporarily disabled.")); + return; + } + + KickerData *kd = ci->Require("kickerdata"); + if (params[1].equals_ci("ON")) + { + bool override = !access.HasPriv("SET"); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable dontkickvoices"; + + kd->dontkickvoices = true; + source.Reply(_("Bot \002won't kick voices\002 on channel %s."), ci->name.c_str()); + } + else if (params[1].equals_ci("OFF")) + { + bool override = !access.HasPriv("SET"); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable dontkickvoices"; + + kd->dontkickvoices = false; + source.Reply(_("Bot \002will kick voices\002 on channel %s."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, source.command); + + kd->Check(ci); + } + + bool OnHelp(CommandSource &source, const Anope::string &) anope_override + { + this->SendSyntax(source); + source.Reply(_(" \n" + "Enables or disables \002voices protection\002 mode on a channel.\n" + "When it is enabled, voices won't be kicked by the bot\n" + "even if they don't match the NOKICK level.")); + return true; + } +}; + +struct BanData { struct Data { @@ -612,11 +881,6 @@ struct BanData : ExtensibleItem int16_t ttb[TTB_SIZE]; Data() - { - this->Clear(); - } - - void Clear() { last_use = 0; for (int i = 0; i < TTB_SIZE; ++i) @@ -625,10 +889,12 @@ struct BanData : ExtensibleItem }; private: - typedef std::map data_type; + typedef Anope::map data_type; data_type data_map; public: + BanData(Extensible *) { } + Data &get(const Anope::string &key) { return this->data_map[key]; @@ -654,14 +920,9 @@ struct BanData : ExtensibleItem } }; -struct UserData : ExtensibleItem +struct UserData { - UserData() - { - this->Clear(); - } - - void Clear() + UserData(Extensible *) { last_use = last_start = Anope::CurTime; lines = times = 0; @@ -682,7 +943,6 @@ struct UserData : ExtensibleItem Anope::string lastline; }; - class BanDataPurger : public Timer { public: @@ -696,12 +956,12 @@ class BanDataPurger : public Timer { Channel *c = it->second; - BanData *bd = c->GetExt("bs_main_bandata"); + BanData *bd = c->GetExt("bandata"); if (bd != NULL) { bd->purge(); if (bd->empty()) - c->Shrink("bs_main_bandata"); + c->Shrink("bandata"); } } } @@ -709,6 +969,10 @@ class BanDataPurger : public Timer class BSKick : public Module { + ExtensibleItem bandata; + ExtensibleItem userdata; + KickerDataImpl::ExtensibleItem kickerdata; + CommandBSKick commandbskick; CommandBSKickAMSG commandbskickamsg; CommandBSKickBadwords commandbskickbadwords; @@ -721,17 +985,14 @@ class BSKick : public Module CommandBSKickReverses commandbskickreverse; CommandBSKickUnderlines commandbskickunderlines; + CommandBSSetDontKickOps commandbssetdontkickops; + CommandBSSetDontKickVoices commandbssetdontkickvoices; + BanDataPurger purger; BanData::Data &GetBanData(User *u, Channel *c) { - BanData *bd = c->GetExt("bs_main_bandata"); - if (bd == NULL) - { - bd = new BanData(); - c->Extend("bs_main_bandata", bd); - } - + BanData *bd = bandata.Require(c); return bd->get(u->GetMask()); } @@ -741,17 +1002,11 @@ class BSKick : public Module if (uc == NULL) return NULL; - UserData *ud = uc->GetExt("bs_main_userdata"); - if (ud == NULL) - { - ud = new UserData(); - uc->Extend("bs_main_userdata", ud); - } - + UserData *ud = userdata.Require(uc); return ud; } - void check_ban(ChannelInfo *ci, User *u, int ttbtype) + void check_ban(ChannelInfo *ci, User *u, KickerData *kd, int ttbtype) { /* Don't ban ulines */ if (u->server->IsULined()) @@ -760,9 +1015,9 @@ class BSKick : public Module BanData::Data &bd = this->GetBanData(u, ci->c); ++bd.ttb[ttbtype]; - if (ci->ttb[ttbtype] && bd.ttb[ttbtype] >= ci->ttb[ttbtype]) + if (kd->ttb[ttbtype] && bd.ttb[ttbtype] >= kd->ttb[ttbtype]) { - /* Should not use == here because bd.ttb[ttbtype] could possibly be > ci->ttb[ttbtype] + /* Should not use == here because bd.ttb[ttbtype] could possibly be > kd->ttb[ttbtype] * if the TTB was changed after it was not set (0) before and the user had already been * kicked a few times. Bug #1056 - Adam */ @@ -793,26 +1048,136 @@ class BSKick : public Module public: BSKick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), + bandata(this, "bandata"), + userdata(this, "userdata"), + kickerdata(this, "kickerdata"), + commandbskick(this), commandbskickamsg(this), commandbskickbadwords(this), commandbskickbolds(this), commandbskickcaps(this), commandbskickcolors(this), commandbskickflood(this), commandbskickitalics(this), commandbskickrepeat(this), commandbskickreverse(this), commandbskickunderlines(this), + commandbssetdontkickops(this), commandbssetdontkickvoices(this), + purger(this) { me = this; } - ~BSKick() + void OnBotInfo(CommandSource &source, BotInfo *bi, ChannelInfo *ci, InfoFormatter &info) anope_override { - for (channel_map::const_iterator cit = ChannelList.begin(), cit_end = ChannelList.end(); cit != cit_end; ++cit) + if (!ci) + return; + + Anope::string enabled = Language::Translate(source.nc, _("Enabled")); + Anope::string disabled = Language::Translate(source.nc, _("Disabled")); + KickerData *kd = kickerdata.Get(ci); + + if (kd && kd->badwords) + { + if (kd->ttb[TTB_BADWORDS]) + info[_("Bad words kicker")] = Anope::printf("%s (%d kick(s) to ban)", enabled.c_str(), kd->ttb[TTB_BADWORDS]); + else + info[_("Bad words kicker")] = enabled; + } + else + info[_("Bad words kicker")] = disabled; + + if (kd && kd->bolds) + { + if (kd->ttb[TTB_BOLDS]) + info[_("Bolds kicker")] = Anope::printf("%s (%d kick(s) to ban)", enabled.c_str(), kd->ttb[TTB_BOLDS]); + else + info[_("Bolds kicker")] = enabled; + } + else + info[_("Bolds kicker")] = disabled; + + if (kd && kd->caps) + { + if (kd->ttb[TTB_CAPS]) + info[_("Caps kicker")] = Anope::printf(_("%s (%d kick(s) to ban; minimum %d/%d%%"), enabled.c_str(), kd->ttb[TTB_CAPS], kd->capsmin, kd->capspercent); + else + info[_("Caps kicker")] = Anope::printf(_("%s (minimum %d/%d%%)"), enabled.c_str(), kd->capsmin, kd->capspercent); + } + else + info[_("Caps kicker")] = disabled; + + if (kd && kd->colors) + { + if (kd->ttb[TTB_COLORS]) + info[_("Colors kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_COLORS]); + else + info[_("Colors kicker")] = enabled; + } + else + info[_("Colors kicker")] = disabled; + + if (kd && kd->flood) + { + if (kd->ttb[TTB_FLOOD]) + info[_("Flood kicker")] = Anope::printf(_("%s (%d kick(s) to ban; %d lines in %ds"), enabled.c_str(), kd->ttb[TTB_FLOOD], kd->floodlines, kd->floodsecs); + else + info[_("Flood kicker")] = Anope::printf(_("%s (%d lines in %ds)"), enabled.c_str(), kd->floodlines, kd->floodsecs); + } + else + info[_("Flood kicker")] = disabled; + + if (kd && kd->repeat) { - Channel *c = cit->second; - for (Channel::ChanUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ++it) - it->second->Shrink("bs_main_userdata"); - c->Shrink("bs_main_bandata"); + if (kd->ttb[TTB_REPEAT]) + info[_("Repeat kicker")] = Anope::printf(_("%s (%d kick(s) to ban; %d times)"), enabled.c_str(), kd->ttb[TTB_REPEAT], kd->repeattimes); + else + info[_("Repeat kicker")] = Anope::printf(_("%s (%d times)"), enabled.c_str(), kd->repeattimes); + } + else + info[_("Repeat kicker")] = disabled; + + if (kd && kd->reverses) + { + if (kd->ttb[TTB_REVERSES]) + info[_("Reverses kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_REVERSES]); + else + info[_("Reverses kicker")] = enabled; } + else + info[_("Reverses kicker")] = disabled; + + if (kd && kd->underlines) + { + if (kd->ttb[TTB_UNDERLINES]) + info[_("Underlines kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_UNDERLINES]); + else + info[_("Underlines kicker")] = enabled; + } + else + info[_("Underlines kicker")] = disabled; + + if (kd && kd->italics) + { + if (kd->ttb[TTB_ITALICS]) + info[_("Italics kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_ITALICS]); + else + info[_("Italics kicker")] = enabled; + } + else + info[_("Italics kicker")] = disabled; + + if (kd && kd->amsgs) + { + if (kd->ttb[TTB_AMSGS]) + info[_("AMSG kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_AMSGS]); + else + info[_("AMSG kicker")] = enabled; + } + else + info[_("AMSG kicker")] = disabled; + + if (kd && kd->dontkickops) + info.AddOption(_("Ops Protection")); + if (kd && kd->dontkickvoices) + info.AddOption(_("Voices Protection")); } void OnPrivmsg(User *u, Channel *c, Anope::string &msg) anope_override @@ -828,12 +1193,15 @@ class BSKick : public Module ChannelInfo *ci = c->ci; if (ci == NULL) return; + KickerData *kd = kickerdata.Get(ci); + if (kd == NULL) + return; if (ci->AccessFor(u).HasPriv("NOKICK")) return; - else if (ci->HasExt("BS_DONTKICKOPS") && (c->HasUserStatus(u, "HALFOP") || c->HasUserStatus(u, "OP") || c->HasUserStatus(u, "PROTECT") || c->HasUserStatus(u, "OWNER"))) + else if (kd->dontkickops && (c->HasUserStatus(u, "HALFOP") || c->HasUserStatus(u, "OP") || c->HasUserStatus(u, "PROTECT") || c->HasUserStatus(u, "OWNER"))) return; - else if (ci->HasExt("BS_DONTKICKVOICES") && c->HasUserStatus(u, "VOICE")) + else if (kd->dontkickvoices && c->HasUserStatus(u, "VOICE")) return; Anope::string realbuf = msg; @@ -851,47 +1219,47 @@ class BSKick : public Module return; /* Bolds kicker */ - if (ci->HasExt("BS_KICK_BOLDS") && realbuf.find(2) != Anope::string::npos) + if (kd->bolds && realbuf.find(2) != Anope::string::npos) { - check_ban(ci, u, TTB_BOLDS); + check_ban(ci, u, kd, TTB_BOLDS); bot_kick(ci, u, _("Don't use bolds on this channel!")); return; } /* Color kicker */ - if (ci->HasExt("BS_KICK_COLORS") && realbuf.find(3) != Anope::string::npos) + if (kd->colors && realbuf.find(3) != Anope::string::npos) { - check_ban(ci, u, TTB_COLORS); + check_ban(ci, u, kd, TTB_COLORS); bot_kick(ci, u, _("Don't use colors on this channel!")); return; } /* Reverses kicker */ - if (ci->HasExt("BS_KICK_REVERSES") && realbuf.find(22) != Anope::string::npos) + if (kd->reverses && realbuf.find(22) != Anope::string::npos) { - check_ban(ci, u, TTB_REVERSES); + check_ban(ci, u, kd, TTB_REVERSES); bot_kick(ci, u, _("Don't use reverses on this channel!")); return; } /* Italics kicker */ - if (ci->HasExt("BS_KICK_ITALICS") && realbuf.find(29) != Anope::string::npos) + if (kd->italics && realbuf.find(29) != Anope::string::npos) { - check_ban(ci, u, TTB_ITALICS); + check_ban(ci, u, kd, TTB_ITALICS); bot_kick(ci, u, _("Don't use italics on this channel!")); return; } /* Underlines kicker */ - if (ci->HasExt("BS_KICK_UNDERLINES") && realbuf.find(31) != Anope::string::npos) + if (kd->underlines && realbuf.find(31) != Anope::string::npos) { - check_ban(ci, u, TTB_UNDERLINES); + check_ban(ci, u, kd, TTB_UNDERLINES); bot_kick(ci, u, _("Don't use underlines on this channel!")); return; } /* Caps kicker */ - if (ci->HasExt("BS_KICK_CAPS") && realbuf.length() >= static_cast(ci->capsmin)) + if (kd->caps && realbuf.length() >= static_cast(kd->capsmin)) { int i = 0, l = 0; @@ -908,26 +1276,27 @@ class BSKick : public Module * percentage of caps to kick for; the rest is ignored. -GD */ - if ((i || l) && i >= ci->capsmin && i * 100 / (i + l) >= ci->capspercent) + if ((i || l) && i >= kd->capsmin && i * 100 / (i + l) >= kd->capspercent) { - check_ban(ci, u, TTB_CAPS); + check_ban(ci, u, kd, TTB_CAPS); bot_kick(ci, u, _("Turn caps lock OFF!")); return; } } /* Bad words kicker */ - if (ci->HasExt("BS_KICK_BADWORDS")) + if (kd->badwords) { bool mustkick = false; + BadWords *badwords = ci->GetExt("badwords"); /* Normalize the buffer */ Anope::string nbuf = Anope::NormalizeBuffer(realbuf); bool casesensitive = Config->GetModule("botserv")->Get("casesensitive"); - for (unsigned i = 0, end = ci->GetBadWordCount(); i < end; ++i) + for (unsigned i = 0; badwords && i < badwords->GetBadWordCount(); ++i) { - const BadWord *bw = ci->GetBadWord(i); + const BadWord *bw = badwords->GetBadWord(i); if (bw->type == BW_ANY && ((casesensitive && nbuf.find(bw->word) != Anope::string::npos) || (!casesensitive && nbuf.find_ci(bw->word) != Anope::string::npos))) mustkick = true; @@ -983,7 +1352,7 @@ class BSKick : public Module if (mustkick) { - check_ban(ci, u, TTB_BADWORDS); + check_ban(ci, u, kd, TTB_BADWORDS); if (Config->GetModule(me)->Get("gentlebadwordreason")) bot_kick(ci, u, _("Watch your language!")); else @@ -999,34 +1368,34 @@ class BSKick : public Module if (ud) { /* Flood kicker */ - if (ci->HasExt("BS_KICK_FLOOD")) + if (kd->flood) { - if (Anope::CurTime - ud->last_start > ci->floodsecs) + if (Anope::CurTime - ud->last_start > kd->floodsecs) { ud->last_start = Anope::CurTime; ud->lines = 0; } ++ud->lines; - if (ud->lines >= ci->floodlines) + if (ud->lines >= kd->floodlines) { - check_ban(ci, u, TTB_FLOOD); + check_ban(ci, u, kd, TTB_FLOOD); bot_kick(ci, u, _("Stop flooding!")); return; } } /* Repeat kicker */ - if (ci->HasExt("BS_KICK_REPEAT")) + if (kd->repeat) { if (!ud->lastline.equals_ci(realbuf)) ud->times = 0; else ++ud->times; - if (ud->times >= ci->repeattimes) + if (ud->times >= kd->repeattimes) { - check_ban(ci, u, TTB_REPEAT); + check_ban(ci, u, kd, TTB_REPEAT); bot_kick(ci, u, _("Stop repeating yourself!")); return; } @@ -1039,9 +1408,9 @@ class BSKick : public Module Channel *chan = it->second->chan; ++it; - if (chan->ci && chan->ci->HasExt("BS_KICK_AMSGS") && !chan->ci->AccessFor(u).HasPriv("NOKICK")) + if (chan->ci && kd->amsgs && !chan->ci->AccessFor(u).HasPriv("NOKICK")) { - check_ban(chan->ci, u, TTB_AMSGS); + check_ban(chan->ci, u, kd, TTB_AMSGS); bot_kick(chan->ci, u, _("Don't use AMSGs!")); } } diff --git a/modules/commands/bs_set.cpp b/modules/commands/bs_set.cpp index fa2bd716f..4e1e77ad1 100644 --- a/modules/commands/bs_set.cpp +++ b/modules/commands/bs_set.cpp @@ -127,314 +127,6 @@ class CommandBSSetBanExpire : public Command } }; -class CommandBSSetDontKickOps : public Command -{ - public: - CommandBSSetDontKickOps(Module *creator, const Anope::string &sname = "botserv/set/dontkickops") : Command(creator, sname, 2, 2) - { - this->SetDesc(_("To protect ops against bot kicks")); - this->SetSyntax(_("\037channel\037 {ON | OFF}")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) anope_override - { - ChannelInfo *ci = ChannelInfo::Find(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - AccessGroup access = source.AccessFor(ci); - if (!source.HasPriv("botserv/administration") && !access.HasPriv("SET")) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (Anope::ReadOnly) - { - source.Reply(_("Sorry, bot option setting is temporarily disabled.")); - return; - } - - if (params[1].equals_ci("ON")) - { - bool override = !access.HasPriv("SET"); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable dontkickops"; - - ci->ExtendMetadata("BS_DONTKICKOPS"); - source.Reply(_("Bot \002won't kick ops\002 on channel %s."), ci->name.c_str()); - } - else if (params[1].equals_ci("OFF")) - { - bool override = !access.HasPriv("SET"); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable dontkickops"; - - ci->Shrink("BS_DONTKICKOPS"); - source.Reply(_("Bot \002will kick ops\002 on channel %s."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, source.command); - } - - bool OnHelp(CommandSource &source, const Anope::string &) anope_override - { - this->SendSyntax(source); - source.Reply(_(" \n" - "Enables or disables \002ops protection\002 mode on a channel.\n" - "When it is enabled, ops won't be kicked by the bot\n" - "even if they don't match the NOKICK level.")); - return true; - } -}; - -class CommandBSSetDontKickVoices : public Command -{ - public: - CommandBSSetDontKickVoices(Module *creator, const Anope::string &sname = "botserv/set/dontkickvoices") : Command(creator, sname, 2, 2) - { - this->SetDesc(_("To protect voices against bot kicks")); - this->SetSyntax(_("\037channel\037 {ON | OFF}")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) anope_override - { - ChannelInfo *ci = ChannelInfo::Find(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - AccessGroup access = source.AccessFor(ci); - if (!source.HasPriv("botserv/administration") && !access.HasPriv("SET")) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (Anope::ReadOnly) - { - source.Reply(_("Sorry, bot option setting is temporarily disabled.")); - return; - } - - if (params[1].equals_ci("ON")) - { - bool override = !access.HasPriv("SET"); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable dontkickvoices"; - - ci->ExtendMetadata("BS_DONTKICKVOICES"); - source.Reply(_("Bot \002won't kick voices\002 on channel %s."), ci->name.c_str()); - } - else if (params[1].equals_ci("OFF")) - { - bool override = !access.HasPriv("SET"); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable dontkickvoices"; - - ci->Shrink("BS_DONTKICKVOICES"); - source.Reply(_("Bot \002will kick voices\002 on channel %s."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, source.command); - } - - bool OnHelp(CommandSource &source, const Anope::string &) anope_override - { - this->SendSyntax(source); - source.Reply(_(" \n" - "Enables or disables \002voices protection\002 mode on a channel.\n" - "When it is enabled, voices won't be kicked by the bot\n" - "even if they don't match the NOKICK level.")); - return true; - } -}; - -class CommandBSSetFantasy : public Command -{ - public: - CommandBSSetFantasy(Module *creator, const Anope::string &sname = "botserv/set/fantasy") : Command(creator, sname, 2, 2) - { - this->SetDesc(_("Enable fantaisist commands")); - this->SetSyntax(_("\037channel\037 {\037ON|OFF\037}")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) anope_override - { - ChannelInfo *ci = ChannelInfo::Find(params[0]); - const Anope::string &value = params[1]; - - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (!source.HasPriv("botserv/administration") && !source.AccessFor(ci).HasPriv("SET")) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (Anope::ReadOnly) - { - source.Reply(_("Sorry, bot option setting is temporarily disabled.")); - return; - } - - if (value.equals_ci("ON")) - { - bool override = !source.AccessFor(ci).HasPriv("SET"); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable fantasy"; - - ci->ExtendMetadata("BS_FANTASY"); - source.Reply(_("Fantasy mode is now \002on\002 on channel %s."), ci->name.c_str()); - } - else if (value.equals_ci("OFF")) - { - bool override = !source.AccessFor(ci).HasPriv("SET"); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable fantasy"; - - ci->Shrink("BS_FANTASY"); - source.Reply(_("Fantasy mode is now \002off\002 on channel %s."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, source.command); - } - - bool OnHelp(CommandSource &source, const Anope::string &) anope_override - { - this->SendSyntax(source); - source.Reply(_(" \n" - "Enables or disables \002fantasy\002 mode on a channel.\n" - "When it is enabled, users will be able to use\n" - "fantasy commands on a channel when prefixed\n" - "with one of the following fantasy characters: \002%s\002\n" - " \n" - "Note that users wanting to use fantaisist\n" - "commands MUST have enough access for both\n" - "the FANTASIA and the command they are executing."), - Config->GetModule("botserv")->Get("fantasycharacter", "!").c_str()); - return true; - } -}; - -class CommandBSSetGreet : public Command -{ - public: - CommandBSSetGreet(Module *creator, const Anope::string &sname = "botserv/set/greet") : Command(creator, sname, 2, 2) - { - this->SetDesc(_("Enable greet messages")); - this->SetSyntax(_("\037channel\037 {\037ON|OFF\037}")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) anope_override - { - ChannelInfo *ci = ChannelInfo::Find(params[0]); - const Anope::string &value = params[1]; - - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (!source.HasPriv("botserv/administration") && !source.AccessFor(ci).HasPriv("SET")) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (Anope::ReadOnly) - { - source.Reply(_("Sorry, bot option setting is temporarily disabled.")); - return; - } - - if (value.equals_ci("ON")) - { - bool override = !source.AccessFor(ci).HasPriv("SET"); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable greets"; - - ci->ExtendMetadata("BS_GREET"); - source.Reply(_("Greet mode is now \002on\002 on channel %s."), ci->name.c_str()); - } - else if (value.equals_ci("OFF")) - { - bool override = !source.AccessFor(ci).HasPriv("SET"); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable greets"; - - ci->Shrink("BS_GREET"); - source.Reply(_("Greet mode is now \002off\002 on channel %s."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, source.command); - } - - bool OnHelp(CommandSource &source, const Anope::string &) anope_override - { - this->SendSyntax(source); - source.Reply(_(" \n" - "Enables or disables \002greet\002 mode on a channel.\n" - "When it is enabled, the bot will display greet\n" - "messages of users joining the channel, provided\n" - "they have enough access to the channel.")); - return true; - } -}; - -class CommandBSSetNoBot : public Command -{ - public: - CommandBSSetNoBot(Module *creator, const Anope::string &sname = "botserv/set/nobot") : Command(creator, sname, 2, 2) - { - this->SetDesc(_("Prevent a bot from being assigned to a channel")); - this->SetSyntax(_("\037channel\037 {\037ON|OFF