Redirect '/FILTER WINDOW <arg>...' to '/FILTERWINDOW <arg>...'
[softsnow_xchat2_filter.git] / SoftSnow_filter.pl
blob8e13b9de725fd0d9558a49bf5b214d8d2d143a8b
1 #!/usr/bin/perl
3 use strict;
4 use warnings;
6 use File::Temp qw(tempfile);
7 use File::Copy qw(move);
10 my $scriptName = "SoftSnow XChat2 Filter";
11 my $scriptVersion = "2.1.2";
12 my $scriptDescr = "Filter out file server announcements and IRC SPAM";
14 my $B = chr 2; # bold
15 my $U = chr 31; # underline
16 my $C = chr 3; # start of color sequence
17 my $R = chr 22; # reverse
18 my $O = chr 15; # reset
20 ### config ###
21 my $filter_file = Xchat::get_info("xchatdir") . "/SoftSnow_filter.conf";
23 my $filter_turned_on = 0; # is filter is turned on
24 my $limit_to_server = ''; # if true limit to given server (host)
25 my $use_filter_allow = 0; # use overrides (ALLOW before DENY)
27 my $filtered_to_window = 0;
28 my $filter_window = "(filtered)";
29 ### end config ###
31 my $filter_commands = 'ON|OFF|STATUS|SERVER|SERVERON|ALL|HELP|DEBUG|PRINT|ALLOW|ADD|DELETE|SAVE|LOAD';
33 my $filter_help = <<"EOF";
34 ${B}/FILTER $filter_commands${B}
35 /FILTER ON|OFF - turns filtering on/off
36 /FILTER HELP - prints this help message
37 /FILTER STATUS - prints if filter is turned on, and with what limits
38 /FILTER DEBUG - shows some info; used in debuggin the filter
39 /FILTER PRINT - prints all the rules
40 /FILTER ALLOW - toggle use of ALLOW rules (before DENY).
41 /FILTER SERVER - limits filtering to current server (host)
42 /FILTER SERVERON - limits to server and turns filter on
43 /FILTER ALL - resumes filtering everywhere i.e. removes limits
44 /FILTER SAVE - saves the rules to the file $filter_file
45 /FILTER LOAD - loads the rules from the file, replacing existing rules
46 /FILTER ADD <rule> - add rule at the end of the DENY rules
47 /FILTER DELETE [<num>] - delete rule number <num>, or last rule
48 /FILTER SHOW [<num>] - show rule number <num>, or last rule
49 /FILTER VERSION - prints the name and version of this script
50 /FILTER WINDOW <arg>... - same as /FILTERWINDOW <arg>...
51 /FILTER without parameter is equivalent to /FILTER STATUS
52 EOF
54 my $filterwindow_commands = 'ON|OFF|CLOSE|HELP|STATUS|DEBUG';
56 my $filterwindow_help = <<"EOF";
57 ${B}/FILTERWINDOW $filterwindow_commands${B}
58 /FILTERWINDOW ON|OFF - turns saving filtered content to ${U}$filter_window${U}
59 /FILTERWINDOW CLOSE - close ${U}$filter_window${U} (and turn off logging)
60 /FILTERWINDOW STATUS - prints if saving to ${U}$filter_window${U} is turned on
61 /FILTERWINDOW HELP - prints this help message
62 /FILTERWINDOW DEBUG - shows some info; used in debugging this part of filter
63 /FILTERWINDOW without parameter is equivalent to /FILTERWINDOW STATUS
64 EOF
66 Xchat::register($scriptName, $scriptVersion, $scriptDescr);
68 Xchat::hook_command("FILTER", \&filter_command_handler,
69 { help_text => $filter_help });
70 Xchat::hook_command("FILTERWINDOW", \&filterwindow_command_handler,
71 { help_text => $filterwindow_help });
72 Xchat::hook_server("PRIVMSG", \&privmsg_handler);
74 Xchat::print("Loading ${B}$scriptName $scriptVersion${B}\n".
75 " For help: ${B}/FILTER HELP${B}\n");
77 # GUI, windows, etc.
78 if ($filtered_to_window) {
79 Xchat::command("QUERY $filter_window");
82 # information about (default) options used
83 if ($filter_turned_on) {
84 Xchat::print("Filter turned ${B}ON${B}\n");
85 } else {
86 Xchat::print("Filter turned ${B}OFF${B}\n");
88 if ($limit_to_server) {
89 Xchat::print("Filter limited to server $limit_to_server\n")
91 if ($use_filter_allow) {
92 Xchat::print("Filter uses ALLOW rules\n");
95 # ------------------------------------------------------------
97 my @filter_allow = (
98 q/^\@search\s/,
101 my @filter_deny = (
102 q/\@/,
103 q/^\s*\!/,
104 q/slot\(s\)/,
105 #q/~&~&~/,
107 #xdcc
108 q/^\#\d+/,
110 #fserves
111 q/(?i)fserve.*trigger/,
112 q/(?i)trigger.*\!/,
113 q/(?i)trigger.*\/ctcp/,
114 q/(?i)type\:\s*\!/,
115 q/(?i)file server online/,
117 #ftps
118 q/(?i)ftp.*l\/p/,
120 #CTCPs
121 q/SLOTS/,
122 q/MP3 /,
124 #messages for when a file is received/failed to receive
125 q/(?i)DEFINITELY had the right stuff to get/,
126 q/(?i)has just received/,
127 q/(?i)I have just received/,
129 #mp3 play messages
130 q/is listening to/,
131 q/\]\-MP3INFO\-\[/,
133 #spammy scripts
134 q/\]\-SpR\-\[/,
135 q/We are BORG/,
137 #general messages
138 q/brave soldier in the war/,
141 # return 1 (true) if text given as argument is to be filtered out
142 sub isFiltered {
143 my $text = shift;
144 my $regexp = '';
146 #strip colour, underline, bold codes, etc.
147 $text = Xchat::strip_code($text);
149 if ($use_filter_allow) {
150 foreach $regexp (@filter_allow) {
151 return 0 if ($text =~ /$regexp/);
155 foreach $regexp (@filter_deny) {
156 return 1 if ($text =~ /$regexp/);
159 return 0;
162 #called when someone says something in the channel
163 #1: address of speaker
164 #2: PRIVMSG constant
165 #3: channel
166 #4: text said (prefixed with :)
167 sub privmsg_handler {
168 # $_[0] - array reference containing the IRC message or command
169 # and arguments broken into words
170 # $_[1] - array reference containing the Nth word to the last word
171 my ($address, $msgtype, $channel) = @{$_[0]};
172 my ($nick, $user, $host) = ($address =~ /^:(.*?)!(.*?)@(.*)$/);
174 my $text = $_[1][3]; # Get server message
176 my $server = Xchat::get_info("host");
178 #-- EXAMPLE RAW COMMANDS: --
179 #chanmsg: [':epitaph!~epitaph@CPE00a0241892b7-CM014480119187.cpe.net.cable.rogers.com', 'PRIVMSG', '#werd', ':mah', 'script', 'is', 'doing', 'stuff.']
180 #action: [':rlz!railz@bzq-199-176.red.bezeqint.net', 'PRIVMSG', '#werd', ':\x01ACTION', 'hugs', 'elhaym', '\x01']
181 #private: [':olene!oqd@girli.sh', 'PRIVMSG', 'epinoodle', ':hey']
184 return Xchat::EAT_NONE unless $filter_turned_on;
185 if ($limit_to_server) {
186 return Xchat::EAT_NONE unless $server eq $limit_to_server;
188 # do not filter out private messages
189 return Xchat::EAT_NONE unless ($channel =~ /^#/);
191 $text =~ s/^://;
193 if (isFiltered($text)) {
194 if (defined $nick && $filtered_to_window) {
195 #Xchat::print($text, $filter_window)
197 my $ctx = Xchat::get_context();
198 Xchat::set_context($filter_window);
199 Xchat::emit_print('Channel Message', $nick, $text);
200 Xchat::set_context($ctx);
202 #return Xchat::EAT_XCHAT;
203 return Xchat::EAT_ALL;
205 return Xchat::EAT_NONE;
209 # ------------------------------------------------------------
211 sub save_filter {
212 my ($fh, $tmpfile) = tempfile($filter_file.'.XXXXXX', UNLINK=>1);
214 unless ($fh) {
215 Xchat::print("${B}FILTER:${B} ".
216 "Couldn't open temporary file $tmpfile to save filter: $!\n");
217 return;
220 Xchat::print("${B}FILTER SAVE >$filter_file${B}\n");
221 foreach my $regexp (@filter_deny) {
222 Xchat::print("/".$regexp."/ saved\n");
223 print $fh $regexp."\n";
226 unless (close $fh) {
227 Xchat::print("${B}FILTER:${B} Couldn't close file to save filter: $!\n");
228 return;
230 #move($tmpfile, $filter_file);
231 rename($tmpfile, $filter_file);
232 Xchat::print("${B}FILTER SAVED ----------${B}\n");
234 return 1;
237 sub load_filter {
238 my $fh;
240 Xchat::print("${B}FILTER:${B} ...loading filter patterns\n");
241 unless (open $fh, '<', $filter_file) {
242 Xchat::print("${B}FILTER:${B} Couldn't open file to load filter: $!\n");
243 return;
246 @filter_deny = <$fh>;
247 map (chomp, @filter_deny);
249 unless (close $fh) {
250 Xchat::print("${B}FILTER:${B} Couldn't close file to load filter: $!\n");
251 return;
254 Xchat::print("${B}FILTER DENY ----------${B}\n");
255 for (my $i = 0; $i <= $#filter_deny; $i++) {
256 Xchat::print(" [$i]: /".$filter_deny[$i]."/\n");
258 Xchat::print("${B}FILTER DENY ----------${B}\n");
261 sub add_rule ( $ ) {
262 my $rule = shift;
264 # always ading rules at the end
265 push @filter_deny, $rule;
268 sub delete_rule ( $ ) {
269 my $num = shift || $#filter_deny;
271 splice @filter_deny, $num, 1;
274 # ============================================================
275 # ------------------------------------------------------------
276 # ............................................................
278 sub cmd_version {
279 Xchat::print("${B}$scriptName $scriptVersion${B}\n");
280 Xchat::print(" * URL: http://github.com/jnareb/softsnow-xchat2-filter\n");
281 Xchat::print(" * URL: http://gitorious.org/projects/softsnow-xchat2-filter\n");
282 Xchat::print(" * URL: http://repo.or.cz/w/softsnow_xchat2_filter.git\n");
285 sub cmd_status {
286 my $server = shift;
288 if ($filter_turned_on) {
289 Xchat::print("Filter is turned ${B}ON${B}\n");
290 } else {
291 Xchat::print("Filter is turned ${B}OFF${B}\n");
293 if ($limit_to_server) {
294 if ($server eq $limit_to_server) {
295 Xchat::print("Filter is limited to ${B}current${B} ".
296 "server $limit_to_server\n");
297 } else {
298 Xchat::print("Filter is limited to server ".
299 "$limit_to_server != $server\n");
302 if ($use_filter_allow) {
303 Xchat::print("Filter is using ALLOW rules (before DENY)\n");
307 sub cmd_debug {
308 Xchat::print("${B}FILTER DEBUG ----------${B}\n");
309 Xchat::print("Channel: ".Xchat::get_info("channel")."\n");
310 Xchat::print("Host: ".Xchat::get_info("host")."\n");
311 Xchat::print("Server: ".Xchat::get_info("server")."\n");
312 Xchat::print("Server Id: ".Xchat::get_info("id")."\n");
313 Xchat::print("Network: ".Xchat::get_info("network")."\n");
314 Xchat::print("\n");
315 Xchat::printf("%3u %s rules\n", scalar(@filter_allow), "allow");
316 Xchat::printf("%3u %s rules\n", scalar(@filter_deny), "deny");
317 Xchat::print("${B}FILTER DEBUG ----------${B}\n");
320 sub cmd_server_limit {
321 my $server = shift;
323 if ($server) {
324 # adding limiting to given (single) server
325 if ($limit_to_server) {
326 Xchat::print("${B}FILTER:${B} Changing server from $limit_to_server to $server\n");
327 } else {
328 Xchat::print("${B}FILTER:${B} Limiting filtering to server $server\n");
330 $limit_to_server = $server;
332 } else {
333 # removing limiting to server
334 if ($limit_to_server) {
335 Xchat::print("Filter: Removing limit to server $limit_to_server\n");
337 $limit_to_server = '';
342 sub cmd_print_rules {
343 Xchat::print("${B}FILTER PRINT ----------${B}\n");
344 Xchat::print("${B}ALLOW${B}".($use_filter_allow ? ' (on)' : ' (off)')."\n");
346 for (my $i = 0; $i <= $#filter_allow; $i++) {
347 Xchat::print("[$i]: /".$filter_allow[$i]."/\n");
349 Xchat::print("${B}DENY${B}\n");
350 for (my $i = 0; $i <= $#filter_deny; $i++) {
351 Xchat::print("[$i]: /".$filter_deny[$i]."/\n");
353 Xchat::print("${B}FILTER PRINT ----------${B}\n");
356 sub cmd_add_rule {
357 my $rule = shift;
359 if ($rule) {
360 add_rule($rule);
361 Xchat::print("${B}FILTER RULE [$#filter_deny]:${B} /$rule/\n");
362 } else {
363 Xchat::print("Syntax: ${B}/FILTER ADD ${U}rule${U}${B} to add\n")
367 sub cmd_delete_rule {
368 my $num = shift;
370 # strip whitespace
371 $num =~ s/^\s*(.*?)\s*$/$1/g if $num;
372 SWITCH: {
373 unless ($num) {
374 Xchat::print("${B}FILTER:${B} deleting /".$filter_deny[-1]."/\n");
375 $#filter_deny--;
376 Xchat::print("${B}FILTER:${B} deleted successfully last rule\n");
377 last SWITCH;
379 if ($num !~ /^\d+$/) {
380 Xchat::print("${B}FILTER:${B} $num is not a number\n");
381 last SWITCH;
383 if ($num < 0 || $num > $#filter_deny) {
384 Xchat::print("${B}FILTER:${B} $num outside range [0,$#filter_deny]\n");
385 last SWITCH;
387 # default
389 Xchat::print("${B}FILTER:${B} deleting /".$filter_deny[$num]."/\n");
390 delete_rule($num);
391 Xchat::print("${B}FILTER:${B} deleted successfully rule $num\n");
396 sub cmd_show_rule {
397 my $num = shift;
399 $num =~ s/^\s*(.*?)\s*$/$1/g if $num;
401 if (defined $num && $num !~ /^\d+$/) {
402 Xchat::print("${B}FILTER:${B} $num is not a number\n");
403 } elsif (defined $num && !defined $filter_deny[$num]) {
404 Xchat::print("${B}FILTER:${B} rule $num does not exist\n");
405 } else {
406 Xchat::print("${B}FILTER:${B} ".(defined $num ? "[$num]" : "last").
407 " rule /".$filter_deny[defined $num ? $num : -1]."/\n");
411 # ============================================================
412 # ============================================================
413 # ============================================================
415 sub filter_command_handler {
416 my $cmd = $_[0][1]; # 1st parameter (after FILTER)
417 my $arg = $_[1][2]; # 2nd word to the last word
418 my $server = Xchat::get_info("host");
421 if (!$cmd || $cmd =~ /^STATUS$/i) {
422 cmd_status($server);
424 } elsif ($cmd =~ /^ON$/i) {
425 $filter_turned_on = 1;
426 Xchat::print("Filter turned ${B}ON${B}\n");
428 } elsif ($cmd =~ /^OFF$/i) {
429 $filter_turned_on = 0;
430 Xchat::print("Filter turned ${B}OFF${B}\n");
432 } elsif ($cmd =~ /^SERVER$/i) {
433 cmd_server_limit($server);
435 } elsif ($cmd =~ /^SERVERON$/i) {
436 cmd_server_limit($server);
438 $filter_turned_on = 1;
439 Xchat::print("Filter turned ${B}ON${B}\n");
441 } elsif ($cmd =~ /^ALL$/i) {
442 cmd_server_limit(undef);
444 } elsif ($cmd =~ /^HELP$/i) {
445 Xchat::print($filter_help);
446 Xchat::print($filterwindow_help);
448 } elsif ($cmd =~ /^VERSION$/i) {
449 cmd_version();
451 } elsif ($cmd =~ /^DEBUG$/i || $cmd =~ /^INFO$/i) {
452 cmd_debug();
454 } elsif ($cmd =~ /^(?:PRINT|LIST)$/i) {
455 cmd_print_rules();
457 } elsif ($cmd =~ /^ALLOW$/i) {
458 $use_filter_allow = !$use_filter_allow;
459 Xchat::print("${B}FILTER:${B} ALLOW rules ".
460 ($use_filter_allow ? "enabled" : "disabled")."\n");
462 } elsif ($cmd =~ /^ADD$/i) {
463 cmd_add_rule($arg);
465 } elsif ($cmd =~ /^DEL(?:ETE)$/i) {
466 cmd_delete_rule($arg);
468 } elsif ($cmd =~ /^SHOW$/i) {
469 cmd_show_rule($arg);
471 } elsif ($cmd =~ /^SAVE$/i) {
472 save_filter();
473 Xchat::print("${B}FILTER:${B} saved DENY rules to $filter_file\n");
475 } elsif ($cmd =~ /^(RE)?LOAD$/i) {
476 load_filter();
477 Xchat::print("${B}FILTER:${B} loaded DENY rules from $filter_file\n");
479 } elsif ($cmd =~ /^WINDOW$/i) {
480 return filterwindow_command_handler(
481 [ 'FILTERWINDOW', @{$_[0]}[2..$#{$_[0]}] ],
482 [ "FILTERWINDOW $_[1][2]", @{$_[1]}[2..$#{$_[1]}] ],
483 $_[2]
486 } else {
487 Xchat::print("Unknown command ${B}/FILTER $_[1][1]${B}\n") if $cmd;
489 return 1;
492 sub filterwindow_command_handler {
493 my $cmd = $_[0][1]; # 1st parameter (after FILTER)
494 #my $arg = $_[1][2]; # 2nd word to the last word
495 my $ctx = Xchat::find_context($filter_window);
497 if (!$cmd || $cmd =~ /^STATUS$/i) {
498 Xchat::print(($filtered_to_window ? "Show" : "Don't show").
499 " filtered content in ".
500 (defined $ctx ? "open" : "closed").
501 " window ${B}$filter_window${B}\n");
503 } elsif ($cmd =~ /^DEBUG$/i) {
504 my $ctx_info = Xchat::context_info($ctx);
505 Xchat::print("${B}FILTERWINDOW DEBUG ----------${B}\n");
506 Xchat::print("filtered_to_window = $filtered_to_window\n");
507 Xchat::print("filter_window = $filter_window\n");
508 if (defined $ctx) {
509 Xchat::print("$filter_window is ${B}open${B}\n");
510 Xchat::print("$filter_window: network => $ctx_info->{network}\n")
511 if defined $ctx_info->{'network'};
512 Xchat::print("$filter_window: host => $ctx_info->{host}\n")
513 if defined $ctx_info->{'host'};
514 Xchat::print("$filter_window: channel => $ctx_info->{channel}\n");
515 Xchat::print("$filter_window: server_id => $ctx_info->{id}\n")
516 if defined $ctx_info->{'id'};
517 } else {
518 Xchat::print("$filter_window is ${B}closed${B}\n");
520 # requires XChat >= 2.8.2
521 #Xchat::print("'Channel Message' format: ".
522 # Xchat::get_info("event_text Channel Message")."\n");
523 #Xchat::print("'Channel Msg Hilight' format: ".
524 # Xchat::get_info("event_text Channel Msg Hilight")."\n");
525 Xchat::print("${B}FILTERWINDOW DEBUG ----------${B}\n");
527 } elsif ($cmd =~ /^ON$/i) {
528 Xchat::command("QUERY $filter_window");
529 Xchat::print("${B}----- START LOGGING FILTERED CONTENTS -----${B}\n",
530 $filter_window)
531 if !$filtered_to_window;
533 $filtered_to_window = 1;
534 Xchat::print("Show filtered content in ${B}$filter_window${B}\n");
536 } elsif ($cmd =~ /^(?:OFF|CLOSE)$/i) {
537 Xchat::print("${B}----- STOP LOGGING FILTERED CONTENTS -----${B}\n",
538 $filter_window)
539 if $filtered_to_window;
540 Xchat::command("CLOSE", $filter_window)
541 if ($cmd =~ /^CLOSE$/i);
543 $filtered_to_window = 0;
544 Xchat::print("Don't show filtered content in ${B}$filter_window${B}\n");
545 Xchat::print("${B}$filter_window${B} closed\n")
546 if ($cmd =~ /^CLOSE$/i);
548 } elsif ($cmd =~ /^HELP$/i) {
549 Xchat::print($filterwindow_help);
551 } else {
552 Xchat::print("Unknown command ${B}/FILTERWINDOW $_[1][1]${B}\n") if $cmd;
553 Xchat::print("${B}${U}USAGE:${U} /FILTERWINDOW $filterwindow_commands${B}\n");
556 return 1;
559 # ======================================================================
560 # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
561 # ----------------------------------------------------------------------
563 Xchat::print("${B}$scriptName $scriptVersion${B} loaded\n");