1###############################################################################
2# badword.pl
3# Copyright (C) 2002  Jan 'fissie' Sembera <fis@ji.cz>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18###############################################################################
19# This is configurable badword script. It may be configured to ban immediately
20# when first badword is detected, or it may count badwords and if number of
21# badwords of given nick exceeds limit, ban him. Badword count may also be
22# expired if no badword is seen for specified period of time. Optional
23# verbosity (let's call it logging) may be enabled as well
24#
25# Runtime variables:
26#
27# badword_channels = list of channels where script is active, separated by space
28# badword_words = list of 'bad words' that trigger this, separated by space
29# badword_reason = reason used in kick when count exceeds permitted limit
30# badword_limit = if number of detected badwords reaches this number, ban'em.
31#                 Set 1 to immediately kickban.
32# badword_clear_delay = if no badword is detected from user for time specified
33#                       here (in seconds), clear his counter. Set 0 to disable.
34# badword_verbose = turns on/off logging features
35# badword_ban_delay = ban after number of kicks specified here. 0 - disables
36#                     banning, 1 - ban immediately, ...
37###############################################################################
38#
39# Changelog:
40#
41# Jun 4 2002
42#    - added ban delaying feature
43#
44###############################################################################
45use Irssi;
46use Irssi::Irc;
47use strict;
48use vars qw($VERSION %IRSSI);
49
50$VERSION = "0.0.3";
51
52%IRSSI = (
53    authors     =>  "Jan 'fissie' Sembera",
54    contact     =>  "fis\@ji.cz",
55    name        =>  "badword",
56    description =>  "Configurable badword kickbanning script",
57    license     =>  "GPL v2 and any later",
58    url         =>  "http://fis.bofh.cz/devel/irssi/",
59);
60
61my %nick_dbase;
62
63sub sig_public {
64  my ($server, $msg, $nick, $address, $target) = @_;
65  my $watch_channels = Irssi::settings_get_str('badword_channels');
66  my $watch_words    = Irssi::settings_get_str('badword_words');
67
68  my @chanz = split (/ /, $watch_channels);
69  my @wordz = split (/ /, $watch_words);
70
71  my $nickrec = $server->channel_find($target)->nick_find($nick);
72  my $nickmode = $nickrec->{op} ? "@" : $nickrec->{voice} ? "+" : "";
73
74  my $aux = 0;
75
76  if (! ($nickmode eq "")) {
77    return;
78  }
79
80  foreach my $ch (@chanz) {
81    if ($ch eq $target) {
82      $aux = 1;
83    }
84  }
85
86  if ($aux == 0) {
87    return;
88  }
89
90  $aux = 0;
91  foreach my $bw (@wordz) {
92#   if (($msg =~ /\ $bw/) || ($msg =~ /^$bw/)) {
93    if ($msg =~ /$bw/) {
94      $aux = 1;
95    }
96  }
97
98  if ($aux == 0) {
99    return;
100  }
101
102  # Ok, here comes badword, check record
103
104  my $luser = $nick_dbase{$nick}{$target};
105
106  if (!$luser) {
107    $nick_dbase{$nick}{$target}{'count'} = 1;
108    $nick_dbase{$nick}{$target}{'kcount'} = 0;
109    $nick_dbase{$nick}{$target}{'stamp'} = time();
110  } else {
111    if ((Irssi::settings_get_int('badword_clear_delay') != 0) && (($nick_dbase{$nick}{$target}{'stamp'})+(Irssi::settings_get_int('badword_clear_delay'))) < time()) {
112      $nick_dbase{$nick}{$target}{'count'} = 1;
113      if (Irssi::settings_get_bool('badword_verbose') == 1) { Irssi::print('BW: Expired for '.$nick.' with hostmask '.$address.' on channel '.$target); }
114    } else {
115      $nick_dbase{$nick}{$target}{'count'} = ($nick_dbase{$nick}{$target}{'count'})+1;
116    }
117    $nick_dbase{$nick}{$target}{'stamp'} = time();
118  }
119
120  $luser = $nick_dbase{$nick}{$target}{'count'};
121
122  if (Irssi::settings_get_bool('badword_verbose') == 1) { Irssi::print('BW: Detected badword from nick '.$nick.' with hostmask '.$address.' on channel '.$target.' - '.$nick_dbase{$nick}{$target}{'count'}.' times'); }
123
124  if ($luser == Irssi::settings_get_int('badword_limit')) {
125    $nick_dbase{$nick}{$target}{'count'} = 0;
126    # Ban'em!
127    my @host = split(/\@/, $address);
128    if (($host[0] =~ /\~/) || ($host[0] =~ /\-/) || ($host[0] =~ /\=/) || ($host[0] =~ /\^/)) { $host[0] = "*"; }
129    my $mask = '*!'.$host[0].'@'.$host[1];
130    $nick_dbase{$nick}{$target}{'kcount'} = ($nick_dbase{$nick}{$target}{'kcount'})+1;
131    if ((Irssi::settings_get_int('badword_ban_delay') > 0) && (Irssi::settings_get_int('badword_ban_delay') == $nick_dbase{$nick}{$target}{'kcount'})) {
132      $server->command('mode '.$target.' +b '.$mask);
133      $nick_dbase{$nick}{$target}{'kcount'} = 0;
134      if (Irssi::settings_get_bool('badword_verbose') == 1) { Irssi::print('BW: Nick '.$nick.' with mask '.$mask.' punished for badwording on channel '.$target.' - banned'); }
135    } else {
136      if (Irssi::settings_get_bool('badword_verbose') == 1) { Irssi::print('BW: Nick '.$nick.' with mask '.$mask.' punished for badwording on channel '.$target.' - kicked'); }
137    }
138    $server->command('quote kick '.$target.' '.$nick.' :'.Irssi::settings_get_str('badword_reason'));
139  }
140}
141
142sub sig_nick {
143  my ($server, $newnick, $nick, $address) = @_;
144
145  $newnick = substr ($newnick, 1) if ($newnick =~ /^:/);
146  my $count = $nick_dbase{$nick};
147  if ($count) {
148    $nick_dbase{$nick} = undef;
149    $nick_dbase{$newnick} = $count;
150    if (Irssi::settings_get_bool('badword_verbose') == 1) { Irssi::print('BW: Tranferred badwords from '.$nick.' to '.$newnick); }
151  }
152}
153
154Irssi::settings_add_str("misc", "badword_channels", "");
155Irssi::settings_add_str("misc", "badword_words", "");
156Irssi::settings_add_str("misc", "badword_reason", "BW: badword limit exceeded");
157Irssi::settings_add_int("misc", "badword_limit", 3);
158Irssi::settings_add_int("misc", "badword_clear_delay", 3600);
159Irssi::settings_add_int("misc", "badword_ban_delay", 1);
160Irssi::settings_add_bool("misc", "badword_verbose", 0);
161
162Irssi::signal_add_last('message public', 'sig_public');
163Irssi::signal_add_last('event nick', 'sig_nick');
164