Anope IRC Services  Version 2.0
bs_kick.cpp
Go to the documentation of this file.
1 /* BotServ core functions
2  *
3  * (C) 2003-2014 Anope Team
4  * Contact us at team@anope.org
5  *
6  * Please read COPYING and README for further details.
7  *
8  * Based on the original code of Epona by Lara.
9  * Based on the original code of Services by Andy Church.
10  *
11  *
12  */
13 
14 #include "module.h"
15 #include "modules/bs_kick.h"
16 #include "modules/bs_badwords.h"
17 
18 static Module *me;
19 
21 {
23  {
25  for (int16_t i = 0; i < TTB_SIZE; ++i)
26  ttb[i] = 0;
27  capsmin = capspercent = 0;
28  floodlines = floodsecs = 0;
29  repeattimes = 0;
30 
31  dontkickops = dontkickvoices = false;
32  }
33 
35  {
36  if (amsgs || badwords || bolds || caps || colors || flood || italics || repeat || reverses || underlines)
37  return;
38 
39  ci->Shrink<KickerData>("kickerdata");
40  }
41 
42  struct ExtensibleItem : ::ExtensibleItem<KickerDataImpl>
43  {
44  ExtensibleItem(Module *m, const Anope::string &ename) : ::ExtensibleItem<KickerDataImpl>(m, ename) { }
45 
47  {
48  if (s->GetSerializableType()->GetName() != "ChannelInfo")
49  return;
50 
52  KickerData *kd = this->Get(ci);
53  if (kd == NULL)
54  return;
55 
56  data["kickerdata:amsgs"] << kd->amsgs;
57  data["kickerdata:badwords"] << kd->badwords;
58  data["kickerdata:bolds"] << kd->bolds;
59  data["kickerdata:caps"] << kd->caps;
60  data["kickerdata:colors"] << kd->colors;
61  data["kickerdata:flood"] << kd->flood;
62  data["kickerdata:italics"] << kd->italics;
63  data["kickerdata:repeat"] << kd->repeat;
64  data["kickerdata:reverses"] << kd->reverses;
65  data["kickerdata:underlines"] << kd->underlines;
66 
67  data.SetType("capsmin", Serialize::Data::DT_INT); data["capsmin"] << kd->capsmin;
68  data.SetType("capspercent", Serialize::Data::DT_INT); data["capspercent"] << kd->capspercent;
69  data.SetType("floodlines", Serialize::Data::DT_INT); data["floodlines"] << kd->floodlines;
70  data.SetType("floodsecs", Serialize::Data::DT_INT); data["floodsecs"] << kd->floodsecs;
71  data.SetType("repeattimes", Serialize::Data::DT_INT); data["repeattimes"] << kd->repeattimes;
72  for (int16_t i = 0; i < TTB_SIZE; ++i)
73  data["ttb"] << kd->ttb[i] << " ";
74  }
75 
77  {
78  if (s->GetSerializableType()->GetName() != "ChannelInfo")
79  return;
80 
82  KickerData *kd = ci->Require<KickerData>("kickerdata");
83 
84  data["kickerdata:amsgs"] >> kd->amsgs;
85  data["kickerdata:badwords"] >> kd->badwords;
86  data["kickerdata:bolds"] >> kd->bolds;
87  data["kickerdata:caps"] >> kd->caps;
88  data["kickerdata:colors"] >> kd->colors;
89  data["kickerdata:flood"] >> kd->flood;
90  data["kickerdata:italics"] >> kd->italics;
91  data["kickerdata:repeat"] >> kd->repeat;
92  data["kickerdata:reverses"] >> kd->reverses;
93  data["kickerdata:underlines"] >> kd->underlines;
94 
95  data["capsmin"] >> kd->capsmin;
96  data["capspercent"] >> kd->capspercent;
97  data["floodlines"] >> kd->floodlines;
98  data["floodsecs"] >> kd->floodsecs;
99  data["repeattimes"] >> kd->repeattimes;
100 
101  Anope::string ttb, tok;
102  data["ttb"] >> ttb;
103  spacesepstream sep(ttb);
104  for (int i = 0; sep.GetToken(tok) && i < TTB_SIZE; ++i)
105  try
106  {
107  kd->ttb[i] = convertTo<int16_t>(tok);
108  }
109  catch (const ConvertException &) { }
110 
111  kd->Check(ci);
112  }
113  };
114 };
115 
116 class CommandBSKick : public Command
117 {
118  public:
119  CommandBSKick(Module *creator) : Command(creator, "botserv/kick", 0)
120  {
121  this->SetDesc(_("Configures kickers"));
122  this->SetSyntax(_("\037option\037 \037channel\037 {\037ON|OFF\037} [\037settings\037]"));
123  }
124 
125  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
126  {
127  this->OnSyntaxError(source, "");
128  }
129 
130  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
131  {
132  this->SendSyntax(source);
133  source.Reply(" ");
134  source.Reply(_("Configures bot kickers. \037option\037 can be one of:"));
135 
136  Anope::string this_name = source.command;
137  for (CommandInfo::map::const_iterator it = source.service->commands.begin(), it_end = source.service->commands.end(); it != it_end; ++it)
138  {
139  const Anope::string &c_name = it->first;
140  const CommandInfo &info = it->second;
141 
142  if (c_name.find_ci(this_name + " ") == 0)
143  {
144  ServiceReference<Command> command("Command", info.name);
145  if (command)
146  {
147  source.command = c_name;
148  command->OnServHelp(source);
149  }
150  }
151  }
152 
153  source.Reply(_("Type \002%s%s HELP %s \037option\037\002 for more information\n"
154  "on a specific option.\n"
155  " \n"
156  "Note: access to this command is controlled by the\n"
157  "level SET."), Config->StrictPrivmsg.c_str(), source.service->nick.c_str(), this_name.c_str());
158 
159  return true;
160  }
161 };
162 
164 {
165  public:
166  CommandBSKickBase(Module *creator, const Anope::string &cname, int minarg, int maxarg) : Command(creator, cname, minarg, maxarg)
167  {
168  }
169 
170  virtual void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override = 0;
171 
172  virtual bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override = 0;
173 
174  protected:
175  bool CheckArguments(CommandSource &source, const std::vector<Anope::string> &params, ChannelInfo* &ci)
176  {
177  const Anope::string &chan = params[0];
178  const Anope::string &option = params[1];
179 
180  ci = ChannelInfo::Find(chan);
181 
182  if (Anope::ReadOnly)
183  source.Reply(_("Sorry, kicker configuration is temporarily disabled."));
184  else if (ci == NULL)
185  source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
186  else if (option.empty())
187  this->OnSyntaxError(source, "");
188  else if (!option.equals_ci("ON") && !option.equals_ci("OFF"))
189  this->OnSyntaxError(source, "");
190  else if (!source.AccessFor(ci).HasPriv("SET") && !source.HasPriv("botserv/administration"))
191  source.Reply(ACCESS_DENIED);
192  else if (!ci->bi)
193  source.Reply(BOT_NOT_ASSIGNED);
194  else
195  return true;
196 
197  return false;
198  }
199 
200  void Process(CommandSource &source, ChannelInfo *ci, const Anope::string &param, const Anope::string &ttb, size_t ttb_idx, const Anope::string &optname, KickerData *kd, bool &val)
201  {
202  if (param.equals_ci("ON"))
203  {
204  if (!ttb.empty())
205  {
206  int16_t i;
207 
208  try
209  {
210  i = convertTo<int16_t>(ttb);
211  if (i < 0)
212  throw ConvertException();
213  }
214  catch (const ConvertException &)
215  {
216  source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str());
217  return;
218  }
219 
220  kd->ttb[ttb_idx] = i;
221  }
222  else
223  kd->ttb[ttb_idx] = 0;
224 
225  val = true;
226  if (kd->ttb[ttb_idx])
227  source.Reply(_("Bot will now kick for \002%s\002, and will place a ban\n"
228  "after %d kicks for the same user."), optname.c_str(), kd->ttb[ttb_idx]);
229  else
230  source.Reply(_("Bot will now kick for \002%s\002."), optname.c_str());
231 
232  bool override = !source.AccessFor(ci).HasPriv("SET");
233  Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable the " << optname << "kicker";
234  }
235  else if (param.equals_ci("OFF"))
236  {
237  bool override = !source.AccessFor(ci).HasPriv("SET");
238  Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable the " << optname << "kicker";
239 
240  val = false;
241  source.Reply(_("Bot won't kick for \002%s\002 anymore."), optname.c_str());
242  }
243  else
244  this->OnSyntaxError(source, "");
245  }
246 };
247 
249 {
250  public:
251  CommandBSKickAMSG(Module *creator) : CommandBSKickBase(creator, "botserv/kick/amsg", 2, 3)
252  {
253  this->SetDesc(_("Configures AMSG kicker"));
254  this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
255  }
256 
257  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
258  {
259  ChannelInfo *ci;
260  if (CheckArguments(source, params, ci))
261  {
262  KickerData *kd = ci->Require<KickerData>("kickerdata");
263  Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_AMSGS, "AMSG", kd, kd->amsgs);
264  kd->Check(ci);
265  }
266  }
267 
268  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
269  {
270  this->SendSyntax(source);
271  source.Reply(" ");
272  BotInfo *bi = Config->GetClient("BotServ");
273  source.Reply(_("Sets the AMSG kicker on or off. When enabled, the bot will\n"
274  "kick users who send the same message to multiple channels\n"
275  "where %s bots are.\n"
276  " \n"
277  "\037ttb\037 is the number of times a user can be kicked\n"
278  "before they get banned. Don't give ttb to disable\n"
279  "the ban system once activated."), bi ? bi->nick.c_str() : "BotServ");
280  return true;
281  }
282 };
283 
285 {
286  public:
287  CommandBSKickBadwords(Module *creator) : CommandBSKickBase(creator, "botserv/kick/badwords", 2, 3)
288  {
289  this->SetDesc(_("Configures badwords kicker"));
290  this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
291  }
292 
293  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
294  {
295  ChannelInfo *ci;
296  if (CheckArguments(source, params, ci))
297  {
298  KickerData *kd = ci->Require<KickerData>("kickerdata");
299  Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_BADWORDS, "badwords", kd, kd->badwords);
300  kd->Check(ci);
301  }
302 
303  }
304 
305  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
306  {
307  this->SendSyntax(source);
308  source.Reply(" ");
309  source.Reply(_("Sets the bad words kicker on or off. When enabled, this\n"
310  "option tells the bot to kick users who say certain words\n"
311  "on the channels.\n"
312  "You can define bad words for your channel using the\n"
313  "\002BADWORDS\002 command. Type \002%s%s HELP BADWORDS\002 for\n"
314  "more information.\n"
315  " \n"
316  "\037ttb\037 is the number of times a user can be kicked\n"
317  "before it gets banned. Don't give ttb to disable\n"
318  "the ban system once activated."), Config->StrictPrivmsg.c_str(), source.service->nick.c_str());
319  return true;
320  }
321 };
322 
324 {
325  public:
326  CommandBSKickBolds(Module *creator) : CommandBSKickBase(creator, "botserv/kick/bolds", 2, 3)
327  {
328  this->SetDesc(_("Configures bolds kicker"));
329  this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
330  }
331 
332  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
333  {
334  ChannelInfo *ci;
335  if (CheckArguments(source, params, ci))
336  {
337  KickerData *kd = ci->Require<KickerData>("kickerdata");
338  Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_BOLDS, "bolds", kd, kd->bolds);
339  kd->Check(ci);
340  }
341  }
342 
343  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
344  {
345  this->SendSyntax(source);
346  source.Reply(" ");
347  source.Reply(_("Sets the bolds kicker on or off. When enabled, this\n"
348  "option tells the bot to kick users who use bolds.\n"
349  " \n"
350  "\037ttb\037 is the number of times a user can be kicked\n"
351  "before it gets banned. Don't give ttb to disable\n"
352  "the ban system once activated."));
353  return true;
354  }
355 };
356 
358 {
359  public:
360  CommandBSKickCaps(Module *creator) : CommandBSKickBase(creator, "botserv/kick/caps", 2, 5)
361  {
362  this->SetDesc(_("Configures caps kicker"));
363  this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037 [\037min\037 [\037percent\037]]]\002"));
364  }
365 
366  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
367  {
368  ChannelInfo *ci;
369  if (!CheckArguments(source, params, ci))
370  return;
371 
372  KickerData *kd = ci->Require<KickerData>("kickerdata");
373 
374  if (params[1].equals_ci("ON"))
375  {
376  const Anope::string &ttb = params.size() > 2 ? params[2] : "",
377  &min = params.size() > 3 ? params[3] : "",
378  &percent = params.size() > 4 ? params[4] : "";
379 
380  if (!ttb.empty())
381  {
382  try
383  {
384  kd->ttb[TTB_CAPS] = convertTo<int16_t>(ttb);
385  if (kd->ttb[TTB_CAPS] < 0)
386  throw ConvertException();
387  }
388  catch (const ConvertException &)
389  {
390  kd->ttb[TTB_CAPS] = 0;
391  source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str());
392  return;
393  }
394  }
395  else
396  kd->ttb[TTB_CAPS] = 0;
397 
398  kd->capsmin = 10;
399  try
400  {
401  kd->capsmin = convertTo<int16_t>(min);
402  }
403  catch (const ConvertException &) { }
404  if (kd->capsmin < 1)
405  kd->capsmin = 10;
406 
407  kd->capspercent = 25;
408  try
409  {
410  kd->capspercent = convertTo<int16_t>(percent);
411  }
412  catch (const ConvertException &) { }
413  if (kd->capspercent < 1 || kd->capspercent > 100)
414  kd->capspercent = 25;
415 
416  kd->caps = true;
417  if (kd->ttb[TTB_CAPS])
418  source.Reply(_("Bot will now kick for \002caps\002 (they must constitute at least\n"
419  "%d characters and %d%% of the entire message), and will\n"
420  "place a ban after %d kicks for the same user."), kd->capsmin, kd->capspercent, kd->ttb[TTB_CAPS]);
421  else
422  source.Reply(_("Bot will now kick for \002caps\002 (they must constitute at least\n"
423  "%d characters and %d%% of the entire message)."), kd->capsmin, kd->capspercent);
424  }
425  else
426  {
427  kd->caps = false;
428  source.Reply(_("Bot won't kick for \002caps\002 anymore."));
429  }
430 
431  kd->Check(ci);
432  }
433 
434  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
435  {
436  this->SendSyntax(source);
437  source.Reply(" ");
438  source.Reply(_("Sets the caps kicker on or off. When enabled, this\n"
439  "option tells the bot to kick users who are talking in\n"
440  "CAPS.\n"
441  "The bot kicks only if there are at least \002min\002 caps\n"
442  "and they constitute at least \002percent\002%% of the total\n"
443  "text line (if not given, it defaults to 10 characters\n"
444  "and 25%%).\n"
445  " \n"
446  "\037ttb\037 is the number of times a user can be kicked\n"
447  "before it gets banned. Don't give ttb to disable\n"
448  "the ban system once activated."));
449  return true;
450  }
451 };
452 
454 {
455  public:
456  CommandBSKickColors(Module *creator) : CommandBSKickBase(creator, "botserv/kick/colors", 2, 3)
457  {
458  this->SetDesc(_("Configures color kicker"));
459  this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
460  }
461 
462  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
463  {
464  ChannelInfo *ci;
465  if (CheckArguments(source, params, ci))
466  {
467  KickerData *kd = ci->Require<KickerData>("kickerdata");
468  Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_COLORS, "colors", kd, kd->colors);
469  kd->Check(ci);
470  }
471  }
472 
473  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
474  {
475  this->SendSyntax(source);
476  source.Reply(" ");
477  source.Reply(_("Sets the colors kicker on or off. When enabled, this\n"
478  "option tells the bot to kick users who use colors.\n"
479  " \n"
480  "\037ttb\037 is the number of times a user can be kicked\n"
481  "before it gets banned. Don't give ttb to disable\n"
482  "the ban system once activated."));
483  return true;
484  }
485 };
486 
488 {
489  public:
490  CommandBSKickFlood(Module *creator) : CommandBSKickBase(creator, "botserv/kick/flood", 2, 5)
491  {
492  this->SetDesc(_("Configures flood kicker"));
493  this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037 [\037ln\037 [\037secs\037]]]\002"));
494  }
495 
496  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
497  {
498  ChannelInfo *ci;
499  if (!CheckArguments(source, params, ci))
500  return;
501 
502  KickerData *kd = ci->Require<KickerData>("kickerdata");
503 
504  if (params[1].equals_ci("ON"))
505  {
506  const Anope::string &ttb = params.size() > 2 ? params[2] : "",
507  &lines = params.size() > 3 ? params[3] : "",
508  &secs = params.size() > 4 ? params[4] : "";
509 
510  if (!ttb.empty())
511  {
512  int16_t i;
513 
514  try
515  {
516  i = convertTo<int16_t>(ttb);
517  if (i < 1)
518  throw ConvertException();
519  }
520  catch (const ConvertException &)
521  {
522  source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str());
523  return;
524  }
525 
526  kd->ttb[TTB_FLOOD] = i;
527  }
528  else
529  kd->ttb[TTB_FLOOD] = 0;
530 
531  kd->floodlines = 6;
532  try
533  {
534  kd->floodlines = convertTo<int16_t>(lines);
535  }
536  catch (const ConvertException &) { }
537  if (kd->floodlines < 2)
538  kd->floodlines = 6;
539 
540  kd->floodsecs = 10;
541  try
542  {
543  kd->floodsecs = convertTo<int16_t>(secs);
544  }
545  catch (const ConvertException &) { }
546  if (kd->floodsecs < 1)
547  kd->floodsecs = 10;
548  if (kd->floodsecs > Config->GetModule(me)->Get<time_t>("keepdata"))
549  kd->floodsecs = Config->GetModule(me)->Get<time_t>("keepdata");
550 
551  kd->flood = true;
552  if (kd->ttb[TTB_FLOOD])
553  source.Reply(_("Bot will now kick for \002flood\002 (%d lines in %d seconds\n"
554  "and will place a ban after %d kicks for the same user."), kd->floodlines, kd->floodsecs, kd->ttb[TTB_FLOOD]);
555  else
556  source.Reply(_("Bot will now kick for \002flood\002 (%d lines in %d seconds)."), kd->floodlines, kd->floodsecs);
557  }
558  else if (params[1].equals_ci("OFF"))
559  {
560  kd->flood = false;
561  source.Reply(_("Bot won't kick for \002flood\002 anymore."));
562  }
563  else
564  this->OnSyntaxError(source, params[1]);
565 
566  kd->Check(ci);
567  }
568 
569  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
570  {
571  this->SendSyntax(source);
572  source.Reply(" ");
573  source.Reply(_("Sets the flood kicker on or off. When enabled, this\n"
574  "option tells the bot to kick users who are flooding\n"
575  "the channel using at least \002ln\002 lines in \002secs\002 seconds\n"
576  "(if not given, it defaults to 6 lines in 10 seconds).\n"
577  " \n"
578  "\037ttb\037 is the number of times a user can be kicked\n"
579  "before it gets banned. Don't give ttb to disable\n"
580  "the ban system once activated."));
581  return true;
582  }
583 };
584 
586 {
587  public:
588  CommandBSKickItalics(Module *creator) : CommandBSKickBase(creator, "botserv/kick/italics", 2, 3)
589  {
590  this->SetDesc(_("Configures italics kicker"));
591  this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
592  }
593 
594  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
595  {
596  ChannelInfo *ci;
597  if (CheckArguments(source, params, ci))
598  {
599  KickerData *kd = ci->Require<KickerData>("kickerdata");
600  Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_ITALICS, "italics", kd, kd->italics);
601  kd->Check(ci);
602  }
603  }
604 
605  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
606  {
607  this->SendSyntax(source);
608  source.Reply(" ");
609  source.Reply(_("Sets the italics kicker on or off. When enabled, this\n"
610  "option tells the bot to kick users who use italics.\n"
611  " \n"
612  "\037ttb\037 is the number of times a user can be kicked\n"
613  "before it gets banned. Don't give ttb to disable\n"
614  "the ban system once activated."));
615  return true;
616  }
617 };
618 
620 {
621  public:
622  CommandBSKickRepeat(Module *creator) : CommandBSKickBase(creator, "botserv/kick/repeat", 2, 4)
623  {
624  this->SetDesc(_("Configures repeat kicker"));
625  this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037 [\037num\037]]\002"));
626  }
627 
628  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
629  {
630  ChannelInfo *ci;
631  if (!CheckArguments(source, params, ci))
632  return;
633 
634  KickerData *kd = ci->Require<KickerData>("kickerdata");
635 
636  if (params[1].equals_ci("ON"))
637  {
638  const Anope::string &ttb = params.size() > 2 ? params[2] : "",
639  &times = params.size() > 3 ? params[3] : "";
640 
641  if (!ttb.empty())
642  {
643  int16_t i;
644 
645  try
646  {
647  i = convertTo<int16_t>(ttb);
648  if (i < 0)
649  throw ConvertException();
650  }
651  catch (const ConvertException &)
652  {
653  source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str());
654  return;
655  }
656 
657  kd->ttb[TTB_REPEAT] = i;
658  }
659  else
660  kd->ttb[TTB_REPEAT] = 0;
661 
662  kd->repeattimes = 3;
663  try
664  {
665  kd->repeattimes = convertTo<int16_t>(times);
666  }
667  catch (const ConvertException &) { }
668  if (kd->repeattimes < 2)
669  kd->repeattimes = 3;
670 
671  kd->repeat = true;
672  if (kd->ttb[TTB_REPEAT])
673  source.Reply(_("Bot will now kick for \002repeats\002 (users that say the\n"
674  "same thing %d times), and will place a ban after %d\n"
675  "kicks for the same user."), kd->repeattimes + 1, kd->ttb[TTB_REPEAT]);
676  else
677  source.Reply(_("Bot will now kick for \002repeats\002 (users that say the\n"
678  "same thing %d times)."), kd->repeattimes + 1);
679  }
680  else if (params[1].equals_ci("OFF"))
681  {
682  kd->repeat = false;
683  source.Reply(_("Bot won't kick for \002repeats\002 anymore."));
684  }
685  else
686  this->OnSyntaxError(source, params[1]);
687 
688  kd->Check(ci);
689  }
690 
691  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
692  {
693  this->SendSyntax(source);
694  source.Reply(" ");
695  source.Reply(_("Sets the repeat kicker on or off. When enabled, this\n"
696  "option tells the bot to kick users who are repeating\n"
697  "themselves \002num\002 times (if num is not given, it\n"
698  "defaults to 3).\n"
699  " \n"
700  "\037ttb\037 is the number of times a user can be kicked\n"
701  "before it gets banned. Don't give ttb to disable\n"
702  "the ban system once activated."));
703  return true;
704  }
705 };
706 
708 {
709  public:
710  CommandBSKickReverses(Module *creator) : CommandBSKickBase(creator, "botserv/kick/reverses", 2, 3)
711  {
712  this->SetDesc(_("Configures reverses kicker"));
713  this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
714  }
715 
716  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
717  {
718  ChannelInfo *ci;
719  if (CheckArguments(source, params, ci))
720  {
721  KickerData *kd = ci->Require<KickerData>("kickerdata");
722  Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_REVERSES, "reverses", kd, kd->reverses);
723  kd->Check(ci);
724  }
725  }
726 
727  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
728  {
729  this->SendSyntax(source);
730  source.Reply(" ");
731  source.Reply(_("Sets the reverses kicker on or off. When enabled, this\n"
732  "option tells the bot to kick users who use reverses.\n"
733  " \n"
734  "\037ttb\037 is the number of times a user can be kicked\n"
735  "before it gets banned. Don't give ttb to disable\n"
736  "the ban system once activated."));
737  return true;
738  }
739 };
740 
742 {
743  public:
744  CommandBSKickUnderlines(Module *creator) : CommandBSKickBase(creator, "botserv/kick/underlines", 2, 3)
745  {
746  this->SetDesc(_("Configures underlines kicker"));
747  this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
748  }
749 
750  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
751  {
752  ChannelInfo *ci;
753  if (CheckArguments(source, params, ci))
754  {
755  KickerData *kd = ci->Require<KickerData>("kickerdata");
756  Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_UNDERLINES, "underlines", kd, kd->underlines);
757  kd->Check(ci);
758  }
759  }
760 
761  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
762  {
763  this->SendSyntax(source);
764  source.Reply(" ");
765  source.Reply(_("Sets the underlines kicker on or off. When enabled, this\n"
766  "option tells the bot to kick users who use underlines.\n"
767  " \n"
768  "\037ttb\037 is the number of times a user can be kicked\n"
769  "before it gets banned. Don't give ttb to disable\n"
770  "the ban system once activated."));
771  return true;
772  }
773 };
774 
776 {
777  public:
778  CommandBSSetDontKickOps(Module *creator, const Anope::string &sname = "botserv/set/dontkickops") : Command(creator, sname, 2, 2)
779  {
780  this->SetDesc(_("To protect ops against bot kicks"));
781  this->SetSyntax(_("\037channel\037 {ON | OFF}"));
782  }
783 
784  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
785  {
786  ChannelInfo *ci = ChannelInfo::Find(params[0]);
787  if (ci == NULL)
788  {
789  source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
790  return;
791  }
792 
793  AccessGroup access = source.AccessFor(ci);
794  if (!source.HasPriv("botserv/administration") && !access.HasPriv("SET"))
795  {
796  source.Reply(ACCESS_DENIED);
797  return;
798  }
799 
800  if (Anope::ReadOnly)
801  {
802  source.Reply(_("Sorry, bot option setting is temporarily disabled."));
803  return;
804  }
805 
806  KickerData *kd = ci->Require<KickerData>("kickerdata");
807  if (params[1].equals_ci("ON"))
808  {
809  bool override = !access.HasPriv("SET");
810  Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable dontkickops";
811 
812  kd->dontkickops = true;
813  source.Reply(_("Bot \002won't kick ops\002 on channel %s."), ci->name.c_str());
814  }
815  else if (params[1].equals_ci("OFF"))
816  {
817  bool override = !access.HasPriv("SET");
818  Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable dontkickops";
819 
820  kd->dontkickops = false;
821  source.Reply(_("Bot \002will kick ops\002 on channel %s."), ci->name.c_str());
822  }
823  else
824  this->OnSyntaxError(source, source.command);
825 
826  kd->Check(ci);
827  }
828 
830  {
831  this->SendSyntax(source);
832  source.Reply(_(" \n"
833  "Enables or disables \002ops protection\002 mode on a channel.\n"
834  "When it is enabled, ops won't be kicked by the bot\n"
835  "even if they don't match the NOKICK level."));
836  return true;
837  }
838 };
839 
841 {
842  public:
843  CommandBSSetDontKickVoices(Module *creator, const Anope::string &sname = "botserv/set/dontkickvoices") : Command(creator, sname, 2, 2)
844  {
845  this->SetDesc(_("To protect voices against bot kicks"));
846  this->SetSyntax(_("\037channel\037 {ON | OFF}"));
847  }
848 
849  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
850  {
851  ChannelInfo *ci = ChannelInfo::Find(params[0]);
852  if (ci == NULL)
853  {
854  source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
855  return;
856  }
857 
858  AccessGroup access = source.AccessFor(ci);
859  if (!source.HasPriv("botserv/administration") && !access.HasPriv("SET"))
860  {
861  source.Reply(ACCESS_DENIED);
862  return;
863  }
864 
865  if (Anope::ReadOnly)
866  {
867  source.Reply(_("Sorry, bot option setting is temporarily disabled."));
868  return;
869  }
870 
871  KickerData *kd = ci->Require<KickerData>("kickerdata");
872  if (params[1].equals_ci("ON"))
873  {
874  bool override = !access.HasPriv("SET");
875  Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable dontkickvoices";
876 
877  kd->dontkickvoices = true;
878  source.Reply(_("Bot \002won't kick voices\002 on channel %s."), ci->name.c_str());
879  }
880  else if (params[1].equals_ci("OFF"))
881  {
882  bool override = !access.HasPriv("SET");
883  Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable dontkickvoices";
884 
885  kd->dontkickvoices = false;
886  source.Reply(_("Bot \002will kick voices\002 on channel %s."), ci->name.c_str());
887  }
888  else
889  this->OnSyntaxError(source, source.command);
890 
891  kd->Check(ci);
892  }
893 
895  {
896  this->SendSyntax(source);
897  source.Reply(_(" \n"
898  "Enables or disables \002voices protection\002 mode on a channel.\n"
899  "When it is enabled, voices won't be kicked by the bot\n"
900  "even if they don't match the NOKICK level."));
901  return true;
902  }
903 };
904 
905 struct BanData
906 {
907  struct Data
908  {
910  time_t last_use;
911  int16_t ttb[TTB_SIZE];
912 
914  {
915  last_use = 0;
916  for (int i = 0; i < TTB_SIZE; ++i)
917  this->ttb[i] = 0;
918  }
919  };
920 
921  private:
923  data_type data_map;
924 
925  public:
927 
928  Data &get(const Anope::string &key)
929  {
930  return this->data_map[key];
931  }
932 
933  bool empty() const
934  {
935  return this->data_map.empty();
936  }
937 
938  void purge()
939  {
940  time_t keepdata = Config->GetModule(me)->Get<time_t>("keepdata");
941  for (data_type::iterator it = data_map.begin(), it_end = data_map.end(); it != it_end;)
942  {
943  const Anope::string &user = it->first;
944  Data &bd = it->second;
945  ++it;
946 
947  if (Anope::CurTime - bd.last_use > keepdata)
948  data_map.erase(user);
949  }
950  }
951 };
952 
953 struct UserData
954 {
956  {
958  lines = times = 0;
959  lastline.clear();
960  }
961 
962  /* Data validity */
963  time_t last_use;
964 
965  /* for flood kicker */
966  int16_t lines;
967  time_t last_start;
968 
969  /* for repeat kicker */
971  int16_t times;
972 
974 };
975 
976 class BanDataPurger : public Timer
977 {
978  public:
979  BanDataPurger(Module *o) : Timer(o, 300, Anope::CurTime, true) { }
980 
981  void Tick(time_t) anope_override
982  {
983  Log(LOG_DEBUG) << "bs_main: Running bandata purger";
984 
985  for (channel_map::iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end; ++it)
986  {
987  Channel *c = it->second;
988 
989  BanData *bd = c->GetExt<BanData>("bandata");
990  if (bd != NULL)
991  {
992  bd->purge();
993  if (bd->empty())
994  c->Shrink<BanData>("bandata");
995  }
996  }
997  }
998 };
999 
1000 class BSKick : public Module
1001 {
1005 
1017 
1020 
1022 
1024  {
1025  BanData *bd = bandata.Require(c);
1026  return bd->get(u->GetMask());
1027  }
1028 
1030  {
1031  ChanUserContainer *uc = c->FindUser(u);
1032  if (uc == NULL)
1033  return NULL;
1034 
1035  UserData *ud = userdata.Require(uc);
1036  return ud;
1037  }
1038 
1039  void check_ban(ChannelInfo *ci, User *u, KickerData *kd, int ttbtype)
1040  {
1041  /* Don't ban ulines or protected users */
1042  if (u->IsProtected())
1043  return;
1044 
1045  BanData::Data &bd = this->GetBanData(u, ci->c);
1046 
1047  ++bd.ttb[ttbtype];
1048  if (kd->ttb[ttbtype] && bd.ttb[ttbtype] >= kd->ttb[ttbtype])
1049  {
1050  /* Should not use == here because bd.ttb[ttbtype] could possibly be > kd->ttb[ttbtype]
1051  * if the TTB was changed after it was not set (0) before and the user had already been
1052  * kicked a few times. Bug #1056 - Adam */
1053 
1054  bd.ttb[ttbtype] = 0;
1055 
1056  Anope::string mask = ci->GetIdealBan(u);
1057 
1058  ci->c->SetMode(NULL, "BAN", mask);
1059  FOREACH_MOD(OnBotBan, (u, ci, mask));
1060  }
1061  }
1062 
1063  void bot_kick(ChannelInfo *ci, User *u, const char *message, ...)
1064  {
1065  va_list args;
1066  char buf[1024];
1067 
1068  if (!ci || !ci->bi || !ci->c || !u || u->IsProtected() || !ci->c->FindUser(u))
1069  return;
1070 
1071  Anope::string fmt = Language::Translate(u, message);
1072  va_start(args, message);
1073  vsnprintf(buf, sizeof(buf), fmt.c_str(), args);
1074  va_end(args);
1075 
1076  ci->c->Kick(ci->bi, u, "%s", buf);
1077  }
1078 
1079  public:
1080  BSKick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
1081  bandata(this, "bandata"),
1082  userdata(this, "userdata"),
1083  kickerdata(this, "kickerdata"),
1084 
1085  commandbskick(this),
1086  commandbskickamsg(this), commandbskickbadwords(this), commandbskickbolds(this), commandbskickcaps(this),
1087  commandbskickcolors(this), commandbskickflood(this), commandbskickitalics(this), commandbskickrepeat(this),
1088  commandbskickreverse(this), commandbskickunderlines(this),
1089 
1090  commandbssetdontkickops(this), commandbssetdontkickvoices(this),
1091 
1092  purger(this)
1093  {
1094  me = this;
1095 
1096  }
1097 
1099  {
1100  if (!ci)
1101  return;
1102 
1103  Anope::string enabled = Language::Translate(source.nc, _("Enabled"));
1104  Anope::string disabled = Language::Translate(source.nc, _("Disabled"));
1105  KickerData *kd = kickerdata.Get(ci);
1106 
1107  if (kd && kd->badwords)
1108  {
1109  if (kd->ttb[TTB_BADWORDS])
1110  info[_("Bad words kicker")] = Anope::printf("%s (%d kick(s) to ban)", enabled.c_str(), kd->ttb[TTB_BADWORDS]);
1111  else
1112  info[_("Bad words kicker")] = enabled;
1113  }
1114  else
1115  info[_("Bad words kicker")] = disabled;
1116 
1117  if (kd && kd->bolds)
1118  {
1119  if (kd->ttb[TTB_BOLDS])
1120  info[_("Bolds kicker")] = Anope::printf("%s (%d kick(s) to ban)", enabled.c_str(), kd->ttb[TTB_BOLDS]);
1121  else
1122  info[_("Bolds kicker")] = enabled;
1123  }
1124  else
1125  info[_("Bolds kicker")] = disabled;
1126 
1127  if (kd && kd->caps)
1128  {
1129  if (kd->ttb[TTB_CAPS])
1130  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);
1131  else
1132  info[_("Caps kicker")] = Anope::printf(_("%s (minimum %d/%d%%)"), enabled.c_str(), kd->capsmin, kd->capspercent);
1133  }
1134  else
1135  info[_("Caps kicker")] = disabled;
1136 
1137  if (kd && kd->colors)
1138  {
1139  if (kd->ttb[TTB_COLORS])
1140  info[_("Colors kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_COLORS]);
1141  else
1142  info[_("Colors kicker")] = enabled;
1143  }
1144  else
1145  info[_("Colors kicker")] = disabled;
1146 
1147  if (kd && kd->flood)
1148  {
1149  if (kd->ttb[TTB_FLOOD])
1150  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);
1151  else
1152  info[_("Flood kicker")] = Anope::printf(_("%s (%d lines in %ds)"), enabled.c_str(), kd->floodlines, kd->floodsecs);
1153  }
1154  else
1155  info[_("Flood kicker")] = disabled;
1156 
1157  if (kd && kd->repeat)
1158  {
1159  if (kd->ttb[TTB_REPEAT])
1160  info[_("Repeat kicker")] = Anope::printf(_("%s (%d kick(s) to ban; %d times)"), enabled.c_str(), kd->ttb[TTB_REPEAT], kd->repeattimes);
1161  else
1162  info[_("Repeat kicker")] = Anope::printf(_("%s (%d times)"), enabled.c_str(), kd->repeattimes);
1163  }
1164  else
1165  info[_("Repeat kicker")] = disabled;
1166 
1167  if (kd && kd->reverses)
1168  {
1169  if (kd->ttb[TTB_REVERSES])
1170  info[_("Reverses kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_REVERSES]);
1171  else
1172  info[_("Reverses kicker")] = enabled;
1173  }
1174  else
1175  info[_("Reverses kicker")] = disabled;
1176 
1177  if (kd && kd->underlines)
1178  {
1179  if (kd->ttb[TTB_UNDERLINES])
1180  info[_("Underlines kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_UNDERLINES]);
1181  else
1182  info[_("Underlines kicker")] = enabled;
1183  }
1184  else
1185  info[_("Underlines kicker")] = disabled;
1186 
1187  if (kd && kd->italics)
1188  {
1189  if (kd->ttb[TTB_ITALICS])
1190  info[_("Italics kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_ITALICS]);
1191  else
1192  info[_("Italics kicker")] = enabled;
1193  }
1194  else
1195  info[_("Italics kicker")] = disabled;
1196 
1197  if (kd && kd->amsgs)
1198  {
1199  if (kd->ttb[TTB_AMSGS])
1200  info[_("AMSG kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_AMSGS]);
1201  else
1202  info[_("AMSG kicker")] = enabled;
1203  }
1204  else
1205  info[_("AMSG kicker")] = disabled;
1206 
1207  if (kd && kd->dontkickops)
1208  info.AddOption(_("Ops protection"));
1209  if (kd && kd->dontkickvoices)
1210  info.AddOption(_("Voices protection"));
1211  }
1212 
1214  {
1215  /* Now we can make kicker stuff. We try to order the checks
1216  * from the fastest one to the slowest one, since there's
1217  * no need to process other kickers if a user is kicked before
1218  * the last kicker check.
1219  *
1220  * But FIRST we check whether the user is protected in any
1221  * way.
1222  */
1223  ChannelInfo *ci = c->ci;
1224  if (ci == NULL)
1225  return;
1226  KickerData *kd = kickerdata.Get(ci);
1227  if (kd == NULL)
1228  return;
1229 
1230  if (ci->AccessFor(u).HasPriv("NOKICK"))
1231  return;
1232  else if (kd->dontkickops && (c->HasUserStatus(u, "HALFOP") || c->HasUserStatus(u, "OP") || c->HasUserStatus(u, "PROTECT") || c->HasUserStatus(u, "OWNER")))
1233  return;
1234  else if (kd->dontkickvoices && c->HasUserStatus(u, "VOICE"))
1235  return;
1236 
1237  Anope::string realbuf = msg;
1238 
1239  /* If it's a /me, cut the CTCP part because the ACTION will cause
1240  * problems with the caps or badwords kicker
1241  */
1242  if (realbuf.substr(0, 8).equals_ci("\1ACTION ") && realbuf[realbuf.length() - 1] == '\1')
1243  {
1244  realbuf.erase(0, 8);
1245  realbuf.erase(realbuf.length() - 1);
1246  }
1247 
1248  if (realbuf.empty())
1249  return;
1250 
1251  /* Bolds kicker */
1252  if (kd->bolds && realbuf.find(2) != Anope::string::npos)
1253  {
1254  check_ban(ci, u, kd, TTB_BOLDS);
1255  bot_kick(ci, u, _("Don't use bolds on this channel!"));
1256  return;
1257  }
1258 
1259  /* Color kicker */
1260  if (kd->colors && realbuf.find(3) != Anope::string::npos)
1261  {
1262  check_ban(ci, u, kd, TTB_COLORS);
1263  bot_kick(ci, u, _("Don't use colors on this channel!"));
1264  return;
1265  }
1266 
1267  /* Reverses kicker */
1268  if (kd->reverses && realbuf.find(22) != Anope::string::npos)
1269  {
1270  check_ban(ci, u, kd, TTB_REVERSES);
1271  bot_kick(ci, u, _("Don't use reverses on this channel!"));
1272  return;
1273  }
1274 
1275  /* Italics kicker */
1276  if (kd->italics && realbuf.find(29) != Anope::string::npos)
1277  {
1278  check_ban(ci, u, kd, TTB_ITALICS);
1279  bot_kick(ci, u, _("Don't use italics on this channel!"));
1280  return;
1281  }
1282 
1283  /* Underlines kicker */
1284  if (kd->underlines && realbuf.find(31) != Anope::string::npos)
1285  {
1286  check_ban(ci, u, kd, TTB_UNDERLINES);
1287  bot_kick(ci, u, _("Don't use underlines on this channel!"));
1288  return;
1289  }
1290 
1291  /* Caps kicker */
1292  if (kd->caps && realbuf.length() >= static_cast<unsigned>(kd->capsmin))
1293  {
1294  int i = 0, l = 0;
1295 
1296  for (unsigned j = 0, end = realbuf.length(); j < end; ++j)
1297  {
1298  if (isupper(realbuf[j]))
1299  ++i;
1300  else if (islower(realbuf[j]))
1301  ++l;
1302  }
1303 
1304  /* i counts uppercase chars, l counts lowercase chars. Only
1305  * alphabetic chars (so islower || isupper) qualify for the
1306  * percentage of caps to kick for; the rest is ignored. -GD
1307  */
1308 
1309  if ((i || l) && i >= kd->capsmin && i * 100 / (i + l) >= kd->capspercent)
1310  {
1311  check_ban(ci, u, kd, TTB_CAPS);
1312  bot_kick(ci, u, _("Turn caps lock OFF!"));
1313  return;
1314  }
1315  }
1316 
1317  /* Bad words kicker */
1318  if (kd->badwords)
1319  {
1320  bool mustkick = false;
1321  BadWords *badwords = ci->GetExt<BadWords>("badwords");
1322 
1323  /* Normalize the buffer */
1324  Anope::string nbuf = Anope::NormalizeBuffer(realbuf);
1325  bool casesensitive = Config->GetModule("botserv")->Get<bool>("casesensitive");
1326 
1327  /* Normalize can return an empty string if this only conains control codes etc */
1328  if (badwords && !nbuf.empty())
1329  for (unsigned i = 0; i < badwords->GetBadWordCount(); ++i)
1330  {
1331  const BadWord *bw = badwords->GetBadWord(i);
1332 
1333  if (bw->word.empty())
1334  continue; // Shouldn't happen
1335 
1336  if (bw->word.length() > nbuf.length())
1337  continue; // This can't ever match
1338 
1339  if (bw->type == BW_ANY && ((casesensitive && nbuf.find(bw->word) != Anope::string::npos) || (!casesensitive && nbuf.find_ci(bw->word) != Anope::string::npos)))
1340  mustkick = true;
1341  else if (bw->type == BW_SINGLE)
1342  {
1343  size_t len = bw->word.length();
1344 
1345  if ((casesensitive && bw->word.equals_cs(nbuf)) || (!casesensitive && bw->word.equals_ci(nbuf)))
1346  mustkick = true;
1347  else if (nbuf.find(' ') == len && ((casesensitive && bw->word.equals_cs(nbuf.substr(0, len))) || (!casesensitive && bw->word.equals_ci(nbuf.substr(0, len)))))
1348  mustkick = true;
1349  else
1350  {
1351  if (len < nbuf.length() && nbuf.rfind(' ') == nbuf.length() - len - 1 && ((casesensitive && nbuf.find(bw->word) == nbuf.length() - len) || (!casesensitive && nbuf.find_ci(bw->word) == nbuf.length() - len)))
1352  mustkick = true;
1353  else
1354  {
1355  Anope::string wordbuf = " " + bw->word + " ";
1356 
1357  if ((casesensitive && nbuf.find(wordbuf) != Anope::string::npos) || (!casesensitive && nbuf.find_ci(wordbuf) != Anope::string::npos))
1358  mustkick = true;
1359  }
1360  }
1361  }
1362  else if (bw->type == BW_START)
1363  {
1364  size_t len = bw->word.length();
1365 
1366  if ((casesensitive && nbuf.substr(0, len).equals_cs(bw->word)) || (!casesensitive && nbuf.substr(0, len).equals_ci(bw->word)))
1367  mustkick = true;
1368  else
1369  {
1370  Anope::string wordbuf = " " + bw->word;
1371 
1372  if ((casesensitive && nbuf.find(wordbuf) != Anope::string::npos) || (!casesensitive && nbuf.find_ci(wordbuf) != Anope::string::npos))
1373  mustkick = true;
1374  }
1375  }
1376  else if (bw->type == BW_END)
1377  {
1378  size_t len = bw->word.length();
1379 
1380  if ((casesensitive && nbuf.substr(nbuf.length() - len).equals_cs(bw->word)) || (!casesensitive && nbuf.substr(nbuf.length() - len).equals_ci(bw->word)))
1381  mustkick = true;
1382  else
1383  {
1384  Anope::string wordbuf = bw->word + " ";
1385 
1386  if ((casesensitive && nbuf.find(wordbuf) != Anope::string::npos) || (!casesensitive && nbuf.find_ci(wordbuf) != Anope::string::npos))
1387  mustkick = true;
1388  }
1389  }
1390 
1391  if (mustkick)
1392  {
1393  check_ban(ci, u, kd, TTB_BADWORDS);
1394  if (Config->GetModule(me)->Get<bool>("gentlebadwordreason"))
1395  bot_kick(ci, u, _("Watch your language!"));
1396  else
1397  bot_kick(ci, u, _("Don't use the word \"%s\" on this channel!"), bw->word.c_str());
1398 
1399  return;
1400  }
1401  } /* for */
1402  } /* if badwords */
1403 
1404  UserData *ud = GetUserData(u, c);
1405 
1406  if (ud)
1407  {
1408  /* Flood kicker */
1409  if (kd->flood)
1410  {
1411  if (Anope::CurTime - ud->last_start > kd->floodsecs)
1412  {
1413  ud->last_start = Anope::CurTime;
1414  ud->lines = 0;
1415  }
1416 
1417  ++ud->lines;
1418  if (ud->lines >= kd->floodlines)
1419  {
1420  check_ban(ci, u, kd, TTB_FLOOD);
1421  bot_kick(ci, u, _("Stop flooding!"));
1422  return;
1423  }
1424  }
1425 
1426  /* Repeat kicker */
1427  if (kd->repeat)
1428  {
1429  if (!ud->lastline.equals_ci(realbuf))
1430  ud->times = 0;
1431  else
1432  ++ud->times;
1433 
1434  if (ud->times >= kd->repeattimes)
1435  {
1436  check_ban(ci, u, kd, TTB_REPEAT);
1437  bot_kick(ci, u, _("Stop repeating yourself!"));
1438  return;
1439  }
1440  }
1441 
1442  if (ud->lastline.equals_ci(realbuf) && !ud->lasttarget.empty() && !ud->lasttarget.equals_ci(ci->name))
1443  {
1444  for (User::ChanUserList::iterator it = u->chans.begin(); it != u->chans.end();)
1445  {
1446  Channel *chan = it->second->chan;
1447  ++it;
1448 
1449  if (chan->ci && kd->amsgs && !chan->ci->AccessFor(u).HasPriv("NOKICK"))
1450  {
1451  check_ban(chan->ci, u, kd, TTB_AMSGS);
1452  bot_kick(chan->ci, u, _("Don't use AMSGs!"));
1453  }
1454  }
1455  }
1456 
1457  ud->lasttarget = ci->name;
1458  ud->lastline = realbuf;
1459  }
1460  }
1461 };
1462 
bool OnHelp(CommandSource &source, const Anope::string &) anope_override
Definition: bs_kick.cpp:829
CommandBSKick commandbskick
Definition: bs_kick.cpp:1006
Definition: bots.h:24
CoreExport bool ReadOnly
Definition: main.cpp:28
BanData(Extensible *)
Definition: bs_kick.cpp:926
bool equals_cs(const char *_str) const
Definition: anope.h:74
CommandBSSetDontKickOps(Module *creator, const Anope::string &sname="botserv/set/dontkickops")
Definition: bs_kick.cpp:778
virtual void Check(ChannelInfo *ci)=0
CommandBSKickFlood commandbskickflood
Definition: bs_kick.cpp:1012
CommandBSKickFlood(Module *creator)
Definition: bs_kick.cpp:490
Anope::string name
Definition: commands.h:32
Definition: timers.h:18
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: bs_kick.cpp:268
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: bs_kick.cpp:569
Definition: hashcomp.h:84
bool flood
Definition: bs_kick.h:32
void clear()
Definition: anope.h:187
data_type data_map
Definition: bs_kick.cpp:923
Anope::string name
Definition: regchannel.h:63
BSKick(const Anope::string &modname, const Anope::string &creator)
Definition: bs_kick.cpp:1080
bool empty() const
Definition: bs_kick.cpp:933
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: bs_kick.cpp:849
Anope::string GetIdealBan(User *u) const
Definition: regchannel.cpp:610
CommandBSKick(Module *creator)
Definition: bs_kick.cpp:119
CommandBSSetDontKickVoices(Module *creator, const Anope::string &sname="botserv/set/dontkickvoices")
Definition: bs_kick.cpp:843
void purge()
Definition: bs_kick.cpp:938
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: bs_kick.cpp:125
CommandBSKickReverses(Module *creator)
Definition: bs_kick.cpp:710
#define ACCESS_DENIED
Definition: language.h:73
CoreExport string printf(const char *fmt,...)
Definition: misc.cpp:536
CommandBSKickBolds commandbskickbolds
Definition: bs_kick.cpp:1009
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: bs_kick.cpp:605
Definition: users.h:34
CommandBSKickBadwords(Module *creator)
Definition: bs_kick.cpp:287
virtual bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override=0
bool repeat
Definition: bs_kick.h:32
Data & get(const Anope::string &key)
Definition: bs_kick.cpp:928
BanDataPurger purger
Definition: bs_kick.cpp:1021
bool caps
Definition: bs_kick.h:32
void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) anope_override
Definition: bs_kick.cpp:76
KickerDataImpl::ExtensibleItem kickerdata
Definition: bs_kick.cpp:1004
int16_t floodlines
Definition: bs_kick.h:35
BadWordType type
Definition: bs_badwords.h:33
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: bs_kick.cpp:293
void SetDesc(const Anope::string &d)
Definition: command.cpp:130
bool equals_ci(const char *_str) const
Definition: anope.h:78
time_t last_start
Definition: bs_kick.cpp:967
Serialize::Reference< ChannelInfo > ci
Definition: channels.h:46
Anope::string word
Definition: bs_badwords.h:32
T * Require(const Anope::string &name)
Definition: extensible.h:244
UserData(Extensible *)
Definition: bs_kick.cpp:955
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: bs_kick.cpp:366
CoreExport time_t CurTime
Definition: main.cpp:41
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: bs_kick.cpp:716
bool colors
Definition: bs_kick.h:32
void OnPrivmsg(User *u, Channel *c, Anope::string &msg) anope_override
Definition: bs_kick.cpp:1213
bool HasPriv(const Anope::string &priv) const
Definition: access.cpp:384
bool reverses
Definition: bs_kick.h:32
#define FOREACH_MOD(ename, args)
Definition: modules.h:62
BanData::Data & GetBanData(User *u, Channel *c)
Definition: bs_kick.cpp:1023
int16_t repeattimes
Definition: bs_kick.h:36
iterator erase(const iterator &i)
Definition: anope.h:155
CommandBSKickBase(Module *creator, const Anope::string &cname, int minarg, int maxarg)
Definition: bs_kick.cpp:166
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: bs_kick.cpp:761
size_type find_ci(const string &_str, size_type pos=0) const
Definition: anope.h:194
static ChannelInfo * Find(const Anope::string &name)
Definition: regchannel.cpp:630
int16_t capsmin
Definition: bs_kick.h:34
UserData * GetUserData(User *u, Channel *c)
Definition: bs_kick.cpp:1029
static Module * me
Definition: bs_kick.cpp:18
ExtensibleItem(Module *m, const Anope::string &ename)
Definition: bs_kick.cpp:44
string substr(size_type pos=0, size_type n=npos) const
Definition: anope.h:277
#define BOT_NOT_ASSIGNED
Definition: language.h:121
T * Require(Extensible *obj)
Definition: extensible.h:116
bool IsProtected()
Definition: users.cpp:724
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: bs_kick.cpp:130
CommandBSKickReverses commandbskickreverse
Definition: bs_kick.cpp:1015
size_type length() const
Definition: anope.h:131
Anope::map< Data > data_type
Definition: bs_kick.cpp:922
Definition: Config.cs:26
Channel * c
Definition: regchannel.h:79
void Shrink(const Anope::string &name)
Definition: extensible.h:253
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: bs_kick.cpp:750
virtual void OnSyntaxError(CommandSource &source, const Anope::string &subcommand)
Definition: command.cpp:191
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: bs_kick.cpp:343
KickerDataImpl(Extensible *obj)
Definition: bs_kick.cpp:22
static const size_type npos
Definition: anope.h:44
BanDataPurger(Module *o)
Definition: bs_kick.cpp:979
CommandBSKickRepeat(Module *creator)
Definition: bs_kick.cpp:622
void ExtensibleSerialize(const Extensible *e, const Serializable *s, Serialize::Data &data) const anope_override
Definition: bs_kick.cpp:46
void Reply(const char *message,...)
Definition: command.cpp:96
CommandBSSetDontKickOps commandbssetdontkickops
Definition: bs_kick.cpp:1018
bool amsgs
Definition: bs_kick.h:32
void check_ban(ChannelInfo *ci, User *u, KickerData *kd, int ttbtype)
Definition: bs_kick.cpp:1039
bool HasPriv(const Anope::string &cmd)
Definition: command.cpp:69
void Check(ChannelInfo *ci) anope_override
Definition: bs_kick.cpp:34
bool dontkickops
Definition: bs_kick.h:38
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: bs_kick.cpp:784
time_t last_use
Definition: bs_kick.cpp:910
CommandBSKickUnderlines(Module *creator)
Definition: bs_kick.cpp:744
Anope::string GetMask() const
Definition: users.cpp:269
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: bs_kick.cpp:462
void OnBotInfo(CommandSource &source, BotInfo *bi, ChannelInfo *ci, InfoFormatter &info) anope_override
Definition: bs_kick.cpp:1098
CoreExport Anope::string NormalizeBuffer(const Anope::string &)
Definition: misc.cpp:664
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: bs_kick.cpp:332
#define anope_override
Definition: services.h:56
bool empty() const
Definition: anope.h:126
CommandBSKickCaps(Module *creator)
Definition: bs_kick.cpp:360
ExtensibleItem< BanData > bandata
Definition: bs_kick.cpp:1002
CommandBSKickBadwords commandbskickbadwords
Definition: bs_kick.cpp:1008
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: bs_kick.cpp:434
virtual BadWord * GetBadWord(unsigned index) const =0
#define MODULE_INIT(x)
Definition: modules.h:45
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: bs_kick.cpp:305
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: bs_kick.cpp:473
CoreExport channel_map ChannelList
Definition: channels.cpp:29
CoreExport const char * Translate(const char *string)
Definition: language.cpp:59
bool bolds
Definition: bs_kick.h:32
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: bs_kick.cpp:496
ChanUserContainer * FindUser(User *u) const
Definition: channels.cpp:173
Anope::string lasttarget
Definition: bs_kick.cpp:970
CommandBSKickAMSG(Module *creator)
Definition: bs_kick.cpp:251
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: bs_kick.cpp:727
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: bs_kick.cpp:628
void SetSyntax(const Anope::string &s)
Definition: command.cpp:140
AccessGroup AccessFor(const User *u)
Definition: regchannel.cpp:413
int16_t ttb[TTB_SIZE]
Definition: bs_kick.h:33
virtual void OnBotBan(User *u, ChannelInfo *ci, const Anope::string &mask)
Definition: modules.h:416
CommandBSKickItalics commandbskickitalics
Definition: bs_kick.cpp:1013
void Tick(time_t) anope_override
Definition: bs_kick.cpp:981
bool italics
Definition: bs_kick.h:32
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: bs_kick.cpp:594
virtual void OnServHelp(CommandSource &source)
Definition: command.cpp:184
virtual unsigned GetBadWordCount() const =0
#define CHAN_X_NOT_REGISTERED
Definition: language.h:84
Anope::string nick
Definition: users.h:62
AccessGroup AccessFor(ChannelInfo *ci)
Definition: command.cpp:41
bool Kick(BotInfo *bi, User *u, const char *reason=NULL,...)
Definition: channels.cpp:762
void bot_kick(ChannelInfo *ci, User *u, const char *message,...)
Definition: bs_kick.cpp:1063
CommandBSKickItalics(Module *creator)
Definition: bs_kick.cpp:588
bool GetToken(Anope::string &token)
Definition: hashcomp.cpp:99
void Process(CommandSource &source, ChannelInfo *ci, const Anope::string &param, const Anope::string &ttb, size_t ttb_idx, const Anope::string &optname, KickerData *kd, bool &val)
Definition: bs_kick.cpp:200
KickerDataImpl * Get(const Extensible *obj) const
Definition: extensible.h:103
T anope_dynamic_static_cast(O ptr)
Definition: anope.h:774
int16_t capspercent
Definition: bs_kick.h:34
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: bs_kick.cpp:257
void SetMode(BotInfo *bi, ChannelMode *cm, const Anope::string &param="", bool enforce_mlock=true)
Definition: channels.cpp:401
CommandBSKickColors(Module *creator)
Definition: bs_kick.cpp:456
void SendSyntax(CommandSource &)
Definition: command.cpp:145
CommandBSKickBolds(Module *creator)
Definition: bs_kick.cpp:326
virtual void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override=0
Anope::string lastline
Definition: bs_kick.cpp:973
const char * c_str() const
Definition: anope.h:117
Definition: logger.h:53
bool badwords
Definition: bs_kick.h:32
time_t last_use
Definition: bs_kick.cpp:963
bool dontkickvoices
Definition: bs_kick.h:38
int16_t times
Definition: bs_kick.cpp:971
CommandBSKickAMSG commandbskickamsg
Definition: bs_kick.cpp:1007
T * GetExt(const Anope::string &name) const
Definition: extensible.h:213
CommandBSKickRepeat commandbskickrepeat
Definition: bs_kick.cpp:1014
ExtensibleItem< UserData > userdata
Definition: bs_kick.cpp:1003
Serialize::Reference< BotInfo > bi
Definition: regchannel.h:82
int16_t lines
Definition: bs_kick.cpp:966
bool underlines
Definition: bs_kick.h:32
CommandBSKickColors commandbskickcolors
Definition: bs_kick.cpp:1011
bool CheckArguments(CommandSource &source, const std::vector< Anope::string > &params, ChannelInfo *&ci)
Definition: bs_kick.cpp:175
CommandBSSetDontKickVoices commandbssetdontkickvoices
Definition: bs_kick.cpp:1019
size_type find(const string &_str, size_type pos=0) const
Definition: anope.h:192
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: bs_kick.cpp:691
#define _(x)
Definition: services.h:50
int16_t floodsecs
Definition: bs_kick.h:35
bool OnHelp(CommandSource &source, const Anope::string &) anope_override
Definition: bs_kick.cpp:894
Definition: anope.h:20
CommandBSKickCaps commandbskickcaps
Definition: bs_kick.cpp:1010
int16_t ttb[TTB_SIZE]
Definition: bs_kick.cpp:911
CommandBSKickUnderlines commandbskickunderlines
Definition: bs_kick.cpp:1016
Anope::string mask
Definition: bs_kick.cpp:909