use normal timeout instead of btimeout
[irc.git] / .irssi / scripts / auto_bleh.pl
1 # bleh stuff
2
3 use Irssi;
4 use Irssi::Irc;
5 use strict;
6 use vars qw($VERSION %IRSSI $DEBUG);
7
8 # 0.05 -- Add IPv6 support
9
10 $VERSION = q$Revision$;
11 %IRSSI = (authors     => 'Don Armstrong',
12           name        => 'auto_bleh',
13           description => 'Provides /ak /aq /ab /abr /abrn /arn /amb /amr /at',
14           license     => 'GPL',
15           changed     => q$Id$,
16          );
17
18 $DEBUG = 1 unless defined $DEBUG;
19
20 my ($actions, %defaults);
21
22 %defaults = (GET_OP       => 1,    # Should we try to get opped when we auto_bleh?
23              USE_CHANSERV => 1,    # Should we use chanserv to get opped?
24              EXPIRE       => 6000,   # Do not try to do anything if the action is more than 6000 seconds old.
25              DEOP         => 1,    # Automatically deop after we've done whatever we were supposed to do.
26              TIMEOUT      => 10,   # Timeout /at bans after 10 minutes
27              SMELLSLIKEFN => qr/freenode.net/,
28             );
29
30 my %command_bindings = (ak   => 'cmd_ak',
31                         ab   => 'cmd_ab',
32                         aq   => 'cmd_aq',
33                         ar   => 'cmd_ar',
34                         abr  => 'cmd_abr',
35                         abk  => 'cmd_abk',
36                         abrn => 'cmd_abrn',
37                         abk  => 'cmd_abkn',
38                         arn  => 'cmd_arn',
39                         amb  => 'cmd_amb',
40                         amr  => 'cmd_amr',
41                         at   => 'cmd_at',
42                        );
43
44 my %bans_to_remove;
45
46
47 sub cmd_at {
48      my ($data, $server, $witem) = @_;
49      return do_auto_bleh([qw(timeout)],$data,$server,$witem);
50 }
51
52 sub cmd_ak {
53      my ($data, $server, $witem) = @_;
54      return do_auto_bleh([qw(kick)],$data,$server,$witem);
55 }
56
57 sub cmd_abk {
58      my ($data, $server, $witem) = @_;
59      return do_auto_bleh([qw(kick ban)],$data,$server,$witem);
60 }
61 sub cmd_abkn {
62      my ($data, $server, $witem) = @_;
63      return do_auto_bleh([qw(kick ban notice)],$data,$server,$witem);
64 }
65
66 sub cmd_amb{
67      my ($data, $server, $witem) = @_;
68      my @nicks = split /\s+/, $data;
69      for (@nicks) {
70           next unless /\w/;
71           do_auto_bleh([qw(ban)],$_,$server,$witem);
72      }
73 }
74
75 sub cmd_ab {
76      my ($data, $server, $witem) = @_;
77      return do_auto_bleh([qw(ban)],$data,$server,$witem);
78 }
79
80 sub cmd_aq {
81      my ($data, $server, $witem) = @_;
82      return do_auto_bleh([qw(quiet)],$data,$server,$witem);
83 }
84
85
86 sub cmd_amr{
87      my ($data, $server, $witem) = @_;
88      my @nicks = split /\s+/, $data;
89      for (@nicks) {
90           next unless /\w/;
91           do_auto_bleh([qw(remove)],$_,$server,$witem);
92      }
93 }
94
95 sub cmd_ar {
96      my ($data, $server, $witem) = @_;
97      return do_auto_bleh([qw(remove)],$data,$server,$witem);
98 }
99 sub cmd_abr{
100      my ($data, $server, $witem) =@_;
101      return do_auto_bleh([qw(remove ban)],$data,$server,$witem);
102 }
103 sub cmd_abrn{
104      my ($data, $server, $witem) =@_;
105      return do_auto_bleh([qw(remove ban notice)],$data,$server,$witem);
106 }
107 sub cmd_arn{
108      my ($data, $server, $witem) =@_;
109      return do_auto_bleh([qw(remove,notice)],$data,$server,$witem);
110 }
111
112
113 sub do_auto_bleh {
114      my ($cmd, $data, $server, $witem, $duration) = @_;
115
116      if (!$server || !$server->{connected}) {
117           Irssi::print("Not connected to server");
118           return;
119      }
120
121      if ($witem->{type} ne 'CHANNEL') {
122           Irssi::print("Can't autokick on a non-channel. [$witem->{type}]");
123           return;
124      }
125
126      if (ref($cmd) eq 'HASH') {
127           # do nothing
128      }
129      elsif (ref($cmd) eq 'ARRAY') {
130           $cmd = {map {$_,1} @$cmd};
131      }
132      elsif (not ref($cmd)) {
133           $cmd = {map {$_,1} split /\s*,\s*/, $cmd};
134      }
135      else {
136           die "Cmd: $cmd option to do_auto_bleh is not a supported type";
137      }
138
139      # Fix up options for opn which doesn't do quiet or remove;
140      # turn them into ban and kick, respectively.
141      if ($server->{address} !~ $defaults{SMELLSLIKEFN}) {
142           my %fn_mapping = (remove  => 'kick',
143                             # OFTC now supports +q/-q.
144                             #quiet   => 'ban',
145                             #unquiet => 'unban',
146                             #timeout => 'btimeout',
147                            );
148           for my $key (keys %fn_mapping) {
149                if ($$cmd{$key}) {
150                     delete $$cmd{$key};
151                     $$cmd{$fn_mapping{$key}} = 1;
152                }
153           }
154      }
155
156      # use Data::Dumper;
157      # Irssi::print(Dumper($data,$server,$witem));
158      # set the network that we're on, the channel and the nick to kick
159      # once we've been opped
160
161      # Heh. Looks like $data needs to be sanitized a bit.
162      $data =~ /^\s*([^\s]+)\s*(\d+)?\s*(.+?|)\s*$/;
163      my $nick = $1;
164      my $timeout = $2;
165      my $reason = $3;
166      $timeout = $defaults{TIMEOUT} if not defined $timeout or $timeout eq '';
167      $reason = 'you should know better' if not defined $reason or $reason eq '';
168
169      my $nick_rec = $witem->nick_find($nick);
170      if (not defined $nick_rec) {
171           Irssi::print("Unable to find nick: $nick");
172      }
173
174      my $hostname = $nick_rec->{host} if defined $nick_rec;
175      Irssi::print("Unable to find hostname for $nick") if not defined $hostname or $hostname eq '';
176      $hostname =~ s/.+\@//;
177
178      Irssi::print("Nick set to '$nick' from '$data', reason set to '$reason'.") if $DEBUG;
179      my $action = {type      => $cmd,
180                    nick      => $nick,
181                    nick_rec  => $nick_rec,
182                    network   => $witem->{server}->{chatnet},
183                    server    => $witem->{server},
184                    completed => 0,
185                    inserted  => time,
186                    channel   => $witem->{name},
187                    reason    => $reason,
188                    hostname  => $hostname,
189                    timeout   => $timeout,
190                   };
191      Irssi::print(i_want($action)) if $DEBUG;
192      if ($witem->{chanop}) { # we seem to be opped.
193           take_action($action,$server,$witem);
194      }
195      else {
196                $actions->{$data.$action->{inserted}}=$action;
197                get_op($server, $action->{channel}) if $defaults{GET_OP};
198      }
199
200 }
201
202 sub get_op {
203      my ($server,$channel) = @_;
204
205      Irssi::print("MSG chanserv op $channel");
206      $server->command("MSG chanserv op $channel") if $defaults{USE_CHANSERV};
207 }
208
209 sub i_want {
210      my $action = shift;
211
212      return "I've wanted to $action->{type} $action->{nick} off $action->{channel} on $action->{network} since $action->{inserted}";
213 }
214
215 sub take_action {
216      my ($action,$server,$channel) = @_;
217
218      my $type = $action->{type};
219      # Now support multiple actions against a single nick (to FE, kick
220      # ban, or remove ban). See /abr foo
221      if ($type->{timeout}) {
222           Irssi::print("Quieting $action->{nick} on $action->{channel} with hostname $action->{hostname} for $action->{timeout} minutes");
223           $server->send_raw("MODE $action->{channel} +q *!*@".$action->{hostname}) if $action->{hostname} ne ''; #quiet hostname
224           $bans_to_remove{"$action->{nick}$action->{inserted}"} = $action;
225      }
226      if ($type->{btimeout}) {
227           Irssi::print("Quieting $action->{nick} on $action->{channel} with hostname $action->{hostname} for $action->{timeout} minutes");
228           $server->send_raw("MODE $action->{channel} +b *!*@".$action->{hostname}) if $action->{hostname} ne ''; #quiet hostname
229           $bans_to_remove{"$action->{nick}$action->{inserted}"} = $action;
230      }
231      if ($type->{quiet}) {
232           Irssi::print("Quieting $action->{nick} on $action->{channel} with hostname $action->{hostname}") if $DEBUG;
233           # Find hostname
234           if ($action->{hostname}) {
235                $server->send_raw("MODE $action->{channel} +q *!*@".$action->{hostname}) if $action->{hostname} ne ''; #quiet hostname
236           }
237
238      }
239      if ($type->{ban}) {
240           Irssi::print("Banning $action->{nick} from $action->{channel} with hostname $action->{hostname}") if $DEBUG;
241           $server->send_raw("MODE $action->{channel} +b *!*@".$action->{hostname}) if $action->{hostname} ne ''; # ban hostname
242      }
243      if ($type->{kick}) {
244           Irssi::print("Kicking $action->{nick} from $action->{channel}") if $DEBUG;
245           if ($action->{reason} =~ /\s/) {
246                $server->send_raw("KICK $action->{channel} $action->{nick} :$action->{reason}");
247           }
248           else {
249                 $server->send_raw("KICK $action->{channel} $action->{nick} $action->{reason}");
250            }
251      }
252      if ($type->{remove}) {
253           Irssi::print("Removing $action->{nick} from $action->{channel}") if $DEBUG;
254           if ($action->{reason} =~ /\s/) {
255                $server->send_raw("REMOVE $action->{channel} $action->{nick} :$action->{reason}");
256           }
257           else {
258                $server->send_raw("REMOVE $action->{channel} $action->{nick} $action->{reason}");
259           }
260      }
261      if ($type->{notice}) {
262           Irssi::print("Noticing $action->{nick} with $action->{reason}") if $DEBUG;
263           $server->command("NOTICE $action->{nick} $action->{reason}");
264      }
265      # unquiet
266      if ($type->{unquiet}) {
267           Irssi::print("Unquieting $action->{nick} on $action->{channel} with hostname $action->{hostname}") if $DEBUG;
268           $server->command("MODE $action->{channel} -q *!*@".$action->{hostname});
269      }
270      # unban
271      if ($type->{unban}) {
272           Irssi::print("Unbanning $action->{nick} on $action->{channel} with hostname $action->{hostname}") if $DEBUG;
273           $server->command("MODE $action->{channel} -b *!*@".$action->{hostname});
274      }
275
276      return; #for now.
277 }
278
279 sub deop_us {
280      my ($rec) = @_;
281      my $channel = $rec->{channel};
282      my $server = $rec->{server};
283
284      #$server->command("MODE $channel->{name} -o $channel->{ownick}->{nick}");
285      if ($channel->{chanop}) {
286           Irssi::print("MODE $channel->{name} -o $channel->{ownnick}->{nick}");
287           $channel->command("/deop $channel->{ownnick}->{nick}");
288      }
289 }
290
291 sub sig_mode_change {
292      my ($channel,$nick) = @_;
293
294      #Irssi::print(Dumper($nick));
295      #Irssi::print(Dumper($channel));
296
297      # Are there any actions to process?
298      # See if we got opped.
299      return if scalar(keys %$actions) eq 0;
300
301      if ($channel->{server}->{nick} eq $nick->{nick} and $nick->{op}) {
302           # Ok, we've been opped, or we are opped now, so do whatever we're supposed to do.
303           Irssi::print("We've been opped") if $DEBUG;
304           # We seem to need some sort of delay here for the chanop stuff to catch up
305           Irssi::timeout_add_once(400,'attempt_actions',undef);
306      }
307      else {
308           Irssi::print("Fooey. Not opped.") if $DEBUG;
309      }
310 }
311
312 sub attempt_actions {
313
314      my @deop_array;
315
316      foreach (keys %$actions) {
317           #Irssi::print(Dumper($actions->{$_}));
318           # See if this action is too old
319           if (time - $actions->{$_}->{inserted} > $defaults{EXPIRE}) {
320                Irssi::print("Expiring action: \"".i_want($actions->{$_})."\" because of time");
321                delete $actions->{$_};
322                next;
323           }
324           Irssi::print(i_want($actions->{$_})) if $DEBUG;
325           # Find the server to take action on
326           my $server = Irssi::server_find_chatnet($actions->{$_}->{network});
327           Irssi::print("Unable to find server for chatnet: $actions->{$_}->{network}") and return if not defined $server;
328           Irssi::print("Found server for chatnet: $actions->{$_}->{network}") if $DEBUG;
329           # Find the channel to take action on
330           my $s_channel = $server->channel_find($actions->{$_}->{channel});
331           Irssi::print("Unable to find channel for channel: $actions->{$_}->{channel}") and return if not defined $s_channel;
332           Irssi::print("Found channel for channel: $actions->{$_}->{channel}") if $DEBUG;
333           # Are we opped on that channel?
334           if ($s_channel->{chanop}) {          # Yes? Take the action against the user.
335                Irssi::print("We are opped on the channel!") if $DEBUG;
336                take_action($actions->{$_},$server,$s_channel);
337                push @deop_array,{server=>$server,channel=>$s_channel} if $defaults{DEOP};
338                delete $actions->{$_}; # Do not repeat this action.
339           }
340           else {
341                Irssi::print("We are not opped on the channel.") if $DEBUG;
342           }
343      }
344      foreach (@deop_array) {
345           deop_us($_);
346      }
347 }
348
349
350 sub try_to_remove_bans {
351      return unless keys %bans_to_remove;
352      for my $key (keys %bans_to_remove) {
353           if (($bans_to_remove{$key}{inserted} + $bans_to_remove{$key}{timeout}*60) < time) {
354                $bans_to_remove{$key}{type} = {unquiet => 1}; #unquiet
355                $actions->{$key} = $bans_to_remove{$key};
356                delete $bans_to_remove{$key};
357                get_op($actions->{$key}{server}, $actions->{$key}{channel}) if $defaults{GET_OP};
358           }
359      }
360 }
361
362 # call the try to remove bans function every minute
363 Irssi::timeout_add(1000*60,'try_to_remove_bans',undef);
364 Irssi::signal_add_last('nick mode changed','sig_mode_change');
365 my ($command,$function);
366
367 while (($command,$function) = each %command_bindings) {
368      Irssi::command_bind($command,$function);
369 }
370
371 1;