1#!/usr/local/bin/perl -w
2# various kick and ban commands
3#  by c0ffee
4#    - http://www.penguin-breeder.org/irssi/
5
6#<scriptinfo>
7use strict;
8use vars qw($VERSION %IRSSI);
9
10use Irssi 20020120;
11$VERSION = "0.27";
12%IRSSI = (
13    authors => "c0ffee",
14    contact => "c0ffee\@penguin-breeder.org",
15    name    => "Various kick and ban commands",
16    description => "Enhances /k /kb and /kn with some nice options.",
17    license => "Public Domain",
18    url     => "http://www.penguin-breeder.org/irssi/",
19    changed => "2017-03-31",
20);
21#</scriptinfo>
22
23my %kickreasons  = (
24    default => ["random kick victim",
25            "no",
26            "are you stupid?",
27            "i don't like you, go away!",
28            "oh, fsck off",
29            "waste other ppls time, elsewhere",
30            "get out and STAY OUT",
31            "don't come back",
32            "no thanks",
33            "on popular demand, you are now leaving the channel",
34            "\$N",
35            "*void*",
36            "/part is the command you're looking for",
37            "this is the irssi of borg. your mIRC will be assimilated. resistance is futile.",
38            "Autokick! mwahahahah!"
39           ],
40    none    => [""],
41    topic   => ["\$topic"],
42);
43
44
45# fine tune the script for different chatnets
46#    cmdline_k      regular expr that matches a correct cmdline for /k
47#    req_chan       0/1 whether the channel is always part of the cmdline
48#    num_nicks      number of nicks ... (-1 = inf)
49#    start_with_dash    0/1 whether the normal cmdline may start with a dash
50#    match_chn      matches channels
51#    match_n        match nicks
52#    match_reason   matches reasons
53#    default_reason reason to give as "no reason"
54my %cfg = (
55    IRC => {
56            cmdline_k       => '\s*([!#+&][^\x0\a\n\r ]*)\s+[-\[\]\\\\\`{}\w_|^\'~]+(,[-\[\]\\\\\`{}\w_|^\'~]+)*\s+\S.*',
57            req_chan        => 0,
58            num_nicks       => 3, # actually, /k takes more, but
59                                  # normal irc servers only take
60                          # three in a row
61            start_with_dash => 1,
62            match_chn       => '([!#+&][^\x0\a\n\r ]*)\s',
63            match_n         => '(?:^|\s+)([-\[\]\\\\\`{}\w_|^\'~]+(?:,[-\[\]\\\\\`{}\w_|^\']+)*)',
64            match_reason    => '^\s*[!#+&][^\x0\a\n\r ]*\s+[-\[\]\\\\\`{}\w_|^\'~]+(?:,[-\[\]\\\\\`{}\w_|^\'~]+)*\s+(\S.*)$',
65            default_reason  => '$N'
66        },
67
68    SILC    => {
69            cmdline_k   => '\s*[^\x0-\x20\*\?,@!]+\s+[^\x0-\x20\*\?,@!]+\s+\S.*',
70            req_chan    => 1,
71            num_nicks   => 1,
72            start_with_dash => 0,
73            match_chn   => '\s*([^\x0-\x20\*\?,@!]+)\s+[^\x0-\x20\*\?,@!]+(?:,[^\x0-\x20\*\?,@!]+)*',
74            match_n     => '\s*(?:[^\x0-\x20\*\?,@!]+\s+)?([^\x0-\x20\*\?,@!]+(?:,[^\x0-\x20\*\?,@!]+)*)',
75            match_reason    => '\s*[^\x0-\x20\*\?,@!]+\s+[^\x0-\x20\*\?,@!]+(?:,[^\x0-\x20\*\?,@!]+)*\s+(\S.*)',
76            default_reason  => '$N'
77        }
78);
79
80sub initialize {
81
82    my $conf_file = Irssi::settings_get_str("kicks_configuration");
83    $conf_file =~ s/~/$ENV{HOME}/;
84    my ($basedir) = $conf_file =~ /^(.*\/).*?/;
85
86    if (-f $conf_file) {
87        open CONF, '<', $conf_file;
88
89        my $line=0;
90
91        while (<CONF>) {
92            $line++;
93
94            next if /^\s*#/;
95
96            chomp;
97            my ($key, $reasons) = /^(\S+)\s+(.+)\s*$/ or next;
98
99            if ($reasons =~ /\`([^\s]+).*?\`/) {
100                $kickreasons{$key} = "$reasons";
101                Irssi::print("Added executable $1 as $key...");
102                next;
103            }
104
105            $reasons =~ s/^"(.*)"$/$1/;
106            $reasons =~ s/~/$ENV{HOME}/;
107            $reasons =~ s/^([^\/])/$basedir$1/;
108
109            if (-f $reasons) {
110
111                $kickreasons{$key} = [];
112
113                open REASON, '<',  $reasons;
114
115                while (<REASON>) {
116                    chomp;
117                    push @{$kickreasons{$key}}, $_;
118                }
119
120                close REASON;
121                Irssi::print("Loaded $reasons as $key...");
122            } else {
123                Irssi::print("can't parse config line $line...");
124            }
125        }
126        close CONF;
127    } else {
128        Irssi::print("Could not find configuration file for kicks...");
129        Irssi::print("... use /set kicks_configuration <file>");
130    }
131}
132
133
134sub get_a_reason {
135    my ($topic) = @_;
136
137
138    return "" if not defined $kickreasons{$topic};
139
140    $_ = eval $kickreasons{$topic}, chomp, s/[\n\t]+/ /mg, return $_
141        if ref($kickreasons{$topic}) ne "ARRAY";
142
143    return $kickreasons{$topic}[rand @{$kickreasons{$topic}}];
144
145}
146
147sub cmd_realkick {
148    my ($data, $server, $channel, $cmd) = @_;
149    my $reasons = "default";
150
151    return if not $server
152              or not defined $cfg{$server->{chat_type}}
153          or not $channel
154              or $data =~ /^$cfg{$server->{chat_type}}{cmdline_k}$/;
155
156    Irssi::signal_stop();
157
158    # let's see whether some options where supplied
159    my $default = Irssi::settings_get_str("default_kick_options");
160    $data = "$default $data" if not $default =~ /^\s*$/;
161    my @opts = split /\s+/, $data;
162    my $opt;
163    my $fail=0;
164
165    while (($opt) = (shift @opts) =~ /^\s*-(\S+)/) {
166
167        $data =~ s/^\s*--\s+//, last if $opt eq "-";
168
169        $data =~ s/^\s*-$opt\s+//,
170            $reasons = lc $opt,
171        next if defined $kickreasons{lc $opt};
172
173        last if $cfg{$server->{chat_type}}{start_with_dash};
174
175        Irssi::print("Unknown option -$opt");
176        $fail = 1;
177
178    }
179
180    return if $fail;
181
182    my $chn = "";
183    ($chn) = $data =~ /^$cfg{$server->{chat_type}}{match_chn}/;
184
185    if ($cfg{$server->{chat_type}}{req_chan} && ($chn eq "")) {
186      Irssi::print "Not joined to any channel";
187      return;
188    }
189
190    # do we need to add a channel?
191    if ($chn eq "") {
192    Irssi::print("Not joined to any channel"), return
193        if $channel->{type} ne "CHANNEL";
194        $chn = $channel->{name};
195
196    $data = "$chn $data";
197    }
198
199    # is a reason already supplied?
200    my $reason;
201    $reason = get_a_reason($reasons)
202        if not (($reason) = $data =~ /$cfg{$server->{chat_type}}{match_reason}/);
203
204    $reason = $cfg{$server->{chat_type}}{default_reason}
205        if $reason =~ /^\s*$/;
206
207    my @nicks = split /,/, ($data =~ /$cfg{$server->{chat_type}}{match_n}/)[0];
208    my $num_nicks = $cfg{$server->{chat_type}}{num_nicks};
209    $num_nicks = @nicks if $num_nicks <= 0;
210
211    my @commands;
212    undef @commands;
213
214    while (@nicks) {
215        my $tmp = ($chn ne "" ? "$chn " : "") .
216               join ",", (splice @nicks,0,$num_nicks);
217        $tmp =~ s/([;\\\$])/\\$1/g;
218        push @commands, "$tmp $reason";
219    }
220
221    foreach (@commands) {
222      if ($_ =~ /^$cfg{$server->{chat_type}}{cmdline_k}$/) {
223        s/\s+$//;
224        $channel->command("EVAL $cmd $_");
225      } else {
226        Irssi::print("BUG: generated invalid $cmd command: $_");
227      }
228    }
229}
230
231sub cmd_kick {
232    my ($data, $server, $channel) = @_;
233
234    cmd_realkick $data, $server, $channel, "KICK";
235
236}
237
238sub cmd_kickban {
239    my ($data, $server, $channel) = @_;
240
241    cmd_realkick $data, $server, $channel, "KICKBAN";
242
243}
244
245Irssi::settings_add_str("misc", "default_kick_options", "");
246Irssi::settings_add_str("misc", "kicks_configuration",
247                Irssi::get_irssi_dir() . "/kicks.conf");
248
249Irssi::command_bind("kick", "cmd_kick");
250Irssi::command_bind("kickban", "cmd_kickban");
251
252initialize();
253
254