fix typo
[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 ".join(',', keys %{$action->{type}||{'do something to'=>undef}}).
213           " $action->{nick} off $action->{channel} on $action->{network} since $action->{inserted}";
214 }
215
216 sub take_action {
217      my ($action,$server,$channel) = @_;
218
219      my $type = $action->{type};
220      # Now support multiple actions against a single nick (to FE, kick
221      # ban, or remove ban). See /abr foo
222      if ($type->{timeout}) {
223           Irssi::print("Quieting $action->{nick} on $action->{channel} with hostname $action->{hostname} for $action->{timeout} minutes");
224           $server->send_raw("MODE $action->{channel} +q *!*@".$action->{hostname}) if $action->{hostname} ne ''; #quiet hostname
225           $bans_to_remove{"$action->{nick}$action->{inserted}"} = $action;
226      }
227      if ($type->{btimeout}) {
228           Irssi::print("Quieting $action->{nick} on $action->{channel} with hostname $action->{hostname} for $action->{timeout} minutes");
229           $server->send_raw("MODE $action->{channel} +b *!*@".$action->{hostname}) if $action->{hostname} ne ''; #quiet hostname
230           $bans_to_remove{"$action->{nick}$action->{inserted}"} = $action;
231      }
232      if ($type->{quiet}) {
233           Irssi::print("Quieting $action->{nick} on $action->{channel} with hostname $action->{hostname}") if $DEBUG;
234           # Find hostname
235           if ($action->{hostname}) {
236                $server->send_raw("MODE $action->{channel} +q *!*@".$action->{hostname}) if $action->{hostname} ne ''; #quiet hostname
237           }
238
239      }
240      if ($type->{ban}) {
241           Irssi::print("Banning $action->{nick} from $action->{channel} with hostname $action->{hostname}") if $DEBUG;
242           $server->send_raw("MODE $action->{channel} +b *!*@".$action->{hostname}) if $action->{hostname} ne ''; # ban hostname
243      }
244      if ($type->{kick}) {
245           Irssi::print("Kicking $action->{nick} from $action->{channel}") if $DEBUG;
246           if ($action->{reason} =~ /\s/) {
247                $server->send_raw("KICK $action->{channel} $action->{nick} :$action->{reason}");
248           }
249           else {
250                 $server->send_raw("KICK $action->{channel} $action->{nick} $action->{reason}");
251            }
252      }
253      if ($type->{remove}) {
254           Irssi::print("Removing $action->{nick} from $action->{channel}") if $DEBUG;
255           if ($action->{reason} =~ /\s/) {
256                $server->send_raw("REMOVE $action->{channel} $action->{nick} :$action->{reason}");
257           }
258           else {
259                $server->send_raw("REMOVE $action->{channel} $action->{nick} $action->{reason}");
260           }
261      }
262      if ($type->{notice}) {
263           Irssi::print("Noticing $action->{nick} with $action->{reason}") if $DEBUG;
264           $server->command("NOTICE $action->{nick} $action->{reason}");
265      }
266      # unquiet
267      if ($type->{unquiet}) {
268           Irssi::print("Unquieting $action->{nick} on $action->{channel} with hostname $action->{hostname}") if $DEBUG;
269           $server->command("MODE $action->{channel} -q *!*@".$action->{hostname});
270      }
271      # unban
272      if ($type->{unban}) {
273           Irssi::print("Unbanning $action->{nick} on $action->{channel} with hostname $action->{hostname}") if $DEBUG;
274           $server->command("MODE $action->{channel} -b *!*@".$action->{hostname});
275      }
276
277      return; #for now.
278 }
279
280 sub deop_us {
281      my ($rec) = @_;
282      my $channel = $rec->{channel};
283      my $server = $rec->{server};
284
285      #$server->command("MODE $channel->{name} -o $channel->{ownick}->{nick}");
286      if ($channel->{chanop}) {
287           Irssi::print("MODE $channel->{name} -o $channel->{ownnick}->{nick}");
288           $channel->command("/deop $channel->{ownnick}->{nick}");
289      }
290 }
291
292 sub sig_mode_change {
293      my ($channel,$nick) = @_;
294
295      #Irssi::print(Dumper($nick));
296      #Irssi::print(Dumper($channel));
297
298      # Are there any actions to process?
299      # See if we got opped.
300      return if scalar(keys %$actions) eq 0;
301
302      if ($channel->{server}->{nick} eq $nick->{nick} and $nick->{op}) {
303           # Ok, we've been opped, or we are opped now, so do whatever we're supposed to do.
304           Irssi::print("We've been opped") if $DEBUG;
305           # We seem to need some sort of delay here for the chanop stuff to catch up
306           Irssi::timeout_add_once(400,'attempt_actions',undef);
307      }
308      else {
309           Irssi::print("Fooey. Not opped.") if $DEBUG;
310      }
311 }
312
313 sub attempt_actions {
314
315      my @deop_array;
316
317      foreach (keys %$actions) {
318           #Irssi::print(Dumper($actions->{$_}));
319           # See if this action is too old
320           if (time - $actions->{$_}->{inserted} > $defaults{EXPIRE}) {
321                Irssi::print("Expiring action: \"".i_want($actions->{$_})."\" because of time");
322                delete $actions->{$_};
323                next;
324           }
325           Irssi::print(i_want($actions->{$_})) if $DEBUG;
326           # Find the server to take action on
327           my $server = Irssi::server_find_chatnet($actions->{$_}->{network});
328           if (not defined $server) {
329                Irssi::print("Unable to find server for chatnet: $actions->{$_}->{network}");
330                return;
331           }
332           Irssi::print("Found server for chatnet: $actions->{$_}->{network}") if $DEBUG;
333           # Find the channel to take action on
334           my $s_channel = $server->channel_find($actions->{$_}->{channel});
335           if (not defined $s_channel){
336                Irssi::print("Unable to find channel for channel: $actions->{$_}->{channel}");
337                return;
338           }
339           Irssi::print("Found channel for channel: $actions->{$_}->{channel}") if $DEBUG;
340           # Are we opped on that channel?
341           if ($s_channel->{chanop}) {          # Yes? Take the action against the user.
342                Irssi::print("We are opped on the channel!") if $DEBUG;
343                take_action($actions->{$_},$server,$s_channel);
344                push @deop_array,{server=>$server,channel=>$s_channel} if $defaults{DEOP};
345                delete $actions->{$_}; # Do not repeat this action.
346           }
347           else {
348                Irssi::print("We are not opped on the channel.") if $DEBUG;
349           }
350      }
351      foreach (@deop_array) {
352           deop_us($_);
353      }
354 }
355
356
357 sub try_to_remove_bans {
358      return unless keys %bans_to_remove;
359      for my $key (keys %bans_to_remove) {
360           if (($bans_to_remove{$key}{inserted} + $bans_to_remove{$key}{timeout}*60) < time) {
361                $bans_to_remove{$key}{type} = {unquiet => 1}; #unquiet
362                $actions->{$key} = $bans_to_remove{$key};
363                delete $bans_to_remove{$key};
364                get_op($actions->{$key}{server}, $actions->{$key}{channel}) if $defaults{GET_OP};
365           }
366      }
367 }
368
369 # call the try to remove bans function every minute
370 Irssi::timeout_add(1000*60,'try_to_remove_bans',undef);
371 Irssi::signal_add_last('nick mode changed','sig_mode_change');
372 my ($command,$function);
373
374 while (($command,$function) = each %command_bindings) {
375      Irssi::command_bind($command,$function);
376 }
377
378 1;