6 use vars qw($VERSION %IRSSI $DEBUG);
8 # 0.05 -- Add IPv6 support
10 $VERSION = q$Revision$;
11 %IRSSI = (authors => 'Don Armstrong',
13 description => 'Provides /ak /aq /ab /abr /abrn /arn /amb /amr /at',
18 $DEBUG = 1 unless defined $DEBUG;
20 my ($actions, %defaults);
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/,
30 my %command_bindings = (ak => 'cmd_ak',
48 my ($data, $server, $witem) = @_;
49 return do_auto_bleh([qw(timeout)],$data,$server,$witem);
53 my ($data, $server, $witem) = @_;
54 return do_auto_bleh([qw(kick)],$data,$server,$witem);
58 my ($data, $server, $witem) = @_;
59 return do_auto_bleh([qw(kick ban)],$data,$server,$witem);
62 my ($data, $server, $witem) = @_;
63 return do_auto_bleh([qw(kick ban notice)],$data,$server,$witem);
67 my ($data, $server, $witem) = @_;
68 my @nicks = split /\s+/, $data;
71 do_auto_bleh([qw(ban)],$_,$server,$witem);
76 my ($data, $server, $witem) = @_;
77 return do_auto_bleh([qw(ban)],$data,$server,$witem);
81 my ($data, $server, $witem) = @_;
82 return do_auto_bleh([qw(quiet)],$data,$server,$witem);
87 my ($data, $server, $witem) = @_;
88 my @nicks = split /\s+/, $data;
91 do_auto_bleh([qw(remove)],$_,$server,$witem);
96 my ($data, $server, $witem) = @_;
97 return do_auto_bleh([qw(remove)],$data,$server,$witem);
100 my ($data, $server, $witem) =@_;
101 return do_auto_bleh([qw(remove ban)],$data,$server,$witem);
104 my ($data, $server, $witem) =@_;
105 return do_auto_bleh([qw(remove ban notice)],$data,$server,$witem);
108 my ($data, $server, $witem) =@_;
109 return do_auto_bleh([qw(remove,notice)],$data,$server,$witem);
114 my ($cmd, $data, $server, $witem, $duration) = @_;
116 if (!$server || !$server->{connected}) {
117 Irssi::print("Not connected to server");
121 if ($witem->{type} ne 'CHANNEL') {
122 Irssi::print("Can't autokick on a non-channel. [$witem->{type}]");
126 if (ref($cmd) eq 'HASH') {
129 elsif (ref($cmd) eq 'ARRAY') {
130 $cmd = {map {$_,1} @$cmd};
132 elsif (not ref($cmd)) {
133 $cmd = {map {$_,1} split /\s*,\s*/, $cmd};
136 die "Cmd: $cmd option to do_auto_bleh is not a supported type";
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.
146 #timeout => 'btimeout',
148 for my $key (keys %fn_mapping) {
151 $$cmd{$fn_mapping{$key}} = 1;
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
161 # Heh. Looks like $data needs to be sanitized a bit.
162 $data =~ /^\s*([^\s]+)\s*(\d+)?\s*(.+?|)\s*$/;
166 $timeout = $defaults{TIMEOUT} if not defined $timeout or $timeout eq '';
167 $reason = 'you should know better' if not defined $reason or $reason eq '';
169 my $nick_rec = $witem->nick_find($nick);
170 if (not defined $nick_rec) {
171 Irssi::print("Unable to find nick: $nick");
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/.+\@//;
178 Irssi::print("Nick set to '$nick' from '$data', reason set to '$reason'.") if $DEBUG;
179 my $action = {type => $cmd,
181 nick_rec => $nick_rec,
182 network => $witem->{server}->{chatnet},
183 server => $witem->{server},
186 channel => $witem->{name},
188 hostname => $hostname,
191 Irssi::print(i_want($action)) if $DEBUG;
192 if ($witem->{chanop}) { # we seem to be opped.
193 take_action($action,$server,$witem);
196 $actions->{$data.$action->{inserted}}=$action;
197 get_op($server, $action->{channel}) if $defaults{GET_OP};
203 my ($server,$channel) = @_;
205 Irssi::print("MSG chanserv op $channel");
206 $server->command("MSG chanserv op $channel") if $defaults{USE_CHANSERV};
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}";
217 my ($action,$server,$channel) = @_;
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;
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;
232 if ($type->{quiet}) {
233 Irssi::print("Quieting $action->{nick} on $action->{channel} with hostname $action->{hostname}") if $DEBUG;
235 if ($action->{hostname}) {
236 $server->send_raw("MODE $action->{channel} +q *!*@".$action->{hostname}) if $action->{hostname} ne ''; #quiet hostname
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
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}");
250 $server->send_raw("KICK $action->{channel} $action->{nick} $action->{reason}");
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}");
259 $server->send_raw("REMOVE $action->{channel} $action->{nick} $action->{reason}");
262 if ($type->{notice}) {
263 Irssi::print("Noticing $action->{nick} with $action->{reason}") if $DEBUG;
264 $server->command("NOTICE $action->{nick} $action->{reason}");
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});
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});
282 my $channel = $rec->{channel};
283 my $server = $rec->{server};
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}");
292 sub sig_mode_change {
293 my ($channel,$nick) = @_;
295 #Irssi::print(Dumper($nick));
296 #Irssi::print(Dumper($channel));
298 # Are there any actions to process?
299 # See if we got opped.
300 return if scalar(keys %$actions) eq 0;
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);
309 Irssi::print("Fooey. Not opped.") if $DEBUG;
313 sub attempt_actions {
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->{$_};
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}");
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}");
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.
348 Irssi::print("We are not opped on the channel.") if $DEBUG;
351 foreach (@deop_array) {
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};
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);
374 while (($command,$function) = each %command_bindings) {
375 Irssi::command_bind($command,$function);