Do not filter out private messages
[softsnow_xchat2_filter.git] / SoftSnow_filter.pl
blob117080d67d80753a7cfe2917354795785ffa6050
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
18 ### config ###
19 my $filter_file = Xchat::get_info("xchatdir") . "/SoftSnow_filter.conf";
21 my $filter_turned_on = 0; # is filter is turned on
22 my $limit_to_server = ''; # if true limit to given server (host)
23 my $use_filter_allow = 0; # use overrides (ALLOW before DENY)
25 my $filtered_to_window = 0;
26 my $filter_window = "(filtered)";
27 ### end config ###
29 my $filter_commands = 'ON|OFF|STATUS|SERVER|SERVERON|ALL|HELP|DEBUG|PRINT|ALLOW|ADD|DELETE|SAVE|LOAD';
31 my $filter_help = <<"EOF";
32 ${B}/FILTER $filter_commands${B}
33 /FILTER ON|OFF - turns filtering on/off
34 /FILTER HELP - prints this help message
35 /FILTER STATUS - prints if filter is turned on, and with what limits
36 /FILTER DEBUG - shows some info; used in debuggin the filter
37 /FILTER PRINT - prints all the rules
38 /FILTER ALLOW - toggle use of ALLOW rules (before DENY).
39 /FILTER SERVER - limits filtering to current server (host)
40 /FILTER SERVERON - limits to server and turns filter on
41 /FILTER ALL - resumes filtering everywhere i.e. removes limits
42 /FILTER SAVE - saves the rules to the file $filter_file
43 /FILTER LOAD - loads the rules from the file, replacing existing rules
44 /FILTER ADD <rule> - add rule at the end of the DENY rules
45 /FILTER DELETE [<num>] - delete rule number <num>, or last rule
46 /FILTER SHOW [<num>] - show rule number <num>, or last rule
47 /FILTER VERSION - prints the name and version of this script
48 /FILTER without parameter is equivalent to /FILTER STATUS
49 EOF
51 my $filterwindow_commands = 'ON|OFF|HELP|STATUS|DEBUG';
53 my $filterwindow_help = <<"EOF";
54 ${B}/FILTERWINDOW $filterwindow_commands${B}
55 /FILTERWINDOW ON|OFF - turns saving filtered content to ${U}$filter_window${U}
56 /FILTERWINDOW STATUS - prints if saving to ${U}$filter_window${U} is turned on
57 /FILTERWINDOW HELP - prints this help message
58 /FILTERWINDOW DEBUG - shows some info; used in debugging this part of filter
59 EOF
61 Xchat::register($scriptName, $scriptVersion, $scriptDescr);
63 Xchat::hook_command("FILTER", \&filter_command_handler,
64 { help_text => $filter_help });
65 Xchat::hook_command("FILTERWINDOW", \&filterwindow_command_handler,
66 { help_text => $filterwindow_help });
67 Xchat::hook_server("PRIVMSG", \&privmsg_handler);
69 Xchat::print("Loading ${B}$scriptName $scriptVersion${B}\n".
70 " For help: ${B}/FILTER HELP${B}\n");
72 # GUI, windows, etc.
73 if ($filtered_to_window) {
74 Xchat::command("QUERY $filter_window");
77 # information about (default) options used
78 if ($filter_turned_on) {
79 Xchat::print("Filter turned ${B}ON${B}\n");
80 } else {
81 Xchat::print("Filter turned ${B}OFF${B}\n");
83 if ($limit_to_server) {
84 Xchat::print("Filter limited to server $limit_to_server\n")
86 if ($use_filter_allow) {
87 Xchat::print("Filter uses ALLOW rules\n");
90 # ------------------------------------------------------------
92 my @filter_allow = (
93 q/^\@search\s/,
96 my @filter_deny = (
97 q/\@/,
98 q/^\s*\!/,
99 q/slot\(s\)/,
100 #q/~&~&~/,
102 #xdcc
103 q/^\#\d+/,
105 #fserves
106 q/(?i)fserve.*trigger/,
107 q/(?i)trigger.*\!/,
108 q/(?i)trigger.*\/ctcp/,
109 q/(?i)type\:\s*\!/,
110 q/(?i)file server online/,
112 #ftps
113 q/(?i)ftp.*l\/p/,
115 #CTCPs
116 q/SLOTS/,
117 q/MP3 /,
119 #messages for when a file is received/failed to receive
120 q/(?i)DEFINITELY had the right stuff to get/,
121 q/(?i)has just received/,
122 q/(?i)I have just received/,
124 #mp3 play messages
125 q/is listening to/,
126 q/\]\-MP3INFO\-\[/,
128 #spammy scripts
129 q/\]\-SpR\-\[/,
130 q/We are BORG/,
132 #general messages
133 q/brave soldier in the war/,
136 # return 1 (true) if text given as argument is to be filtered out
137 sub isFiltered {
138 my $text = shift;
139 my $regexp = '';
141 #strip colour, underline, bold codes, etc.
142 $text = Xchat::strip_code($text);
144 if ($use_filter_allow) {
145 foreach $regexp (@filter_allow) {
146 return 0 if ($text =~ /$regexp/);
150 foreach $regexp (@filter_deny) {
151 return 1 if ($text =~ /$regexp/);
154 return 0;
157 #called when someone says something in the channel
158 #1: address of speaker
159 #2: PRIVMSG constant
160 #3: channel
161 #4: text said (prefixed with :)
162 sub privmsg_handler {
163 # $_[0] - array reference containing the IRC message or command
164 # and arguments broken into words
165 # $_[1] - array reference containing the Nth word to the last word
166 my ($address, $msgtype, $channel) = @{$_[0]};
167 my ($nick, $user, $host) = ($address =~ /^:(.*?)!(.*?)@(.*)$/);
169 my $text = $_[1][3]; # Get server message
171 my $server = Xchat::get_info("host");
173 #-- EXAMPLE RAW COMMANDS: --
174 #chanmsg: [':epitaph!~epitaph@CPE00a0241892b7-CM014480119187.cpe.net.cable.rogers.com', 'PRIVMSG', '#werd', ':mah', 'script', 'is', 'doing', 'stuff.']
175 #action: [':rlz!railz@bzq-199-176.red.bezeqint.net', 'PRIVMSG', '#werd', ':\x01ACTION', 'hugs', 'elhaym', '\x01']
176 #private: [':olene!oqd@girli.sh', 'PRIVMSG', 'epinoodle', ':hey']
179 return Xchat::EAT_NONE unless $filter_turned_on;
180 if ($limit_to_server) {
181 return Xchat::EAT_NONE unless $server eq $limit_to_server;
183 # do not filter out private messages
184 return Xchat::EAT_NONE unless ($_[0][2] =~ /^#/);
186 $text =~ s/^://;
188 if (isFiltered($text)) {
189 if (defined $nick && $filtered_to_window) {
190 #Xchat::print($text, $filter_window)
192 my $ctx = Xchat::get_context();
193 Xchat::set_context($filter_window);
194 Xchat::emit_print('Channel Message', $nick, $text);
195 Xchat::set_context($ctx);
197 #return Xchat::EAT_XCHAT;
198 return Xchat::EAT_ALL;
200 return Xchat::EAT_NONE;
204 # ------------------------------------------------------------
206 sub save_filter {
207 my ($fh, $tmpfile) = tempfile($filter_file.'.XXXXXX', UNLINK=>1);
209 unless ($fh) {
210 Xchat::print("${B}FILTER:${B} ".
211 "Couldn't open temporary file $tmpfile to save filter: $!\n");
212 return;
215 Xchat::print("${B}FILTER SAVE >$filter_file${B}\n");
216 foreach my $regexp (@filter_deny) {
217 Xchat::print("/".$regexp."/ saved\n");
218 print $fh $regexp."\n";
221 unless (close $fh) {
222 Xchat::print("${B}FILTER:${B} Couldn't close file to save filter: $!\n");
223 return;
225 #move($tmpfile, $filter_file);
226 rename($tmpfile, $filter_file);
227 Xchat::print("${B}FILTER SAVED ----------${B}\n");
229 return 1;
232 sub load_filter {
233 my $fh;
235 Xchat::print("${B}FILTER:${B} ...loading filter patterns\n");
236 unless (open $fh, '<', $filter_file) {
237 Xchat::print("${B}FILTER:${B} Couldn't open file to load filter: $!\n");
238 return;
241 @filter_deny = <$fh>;
242 map (chomp, @filter_deny);
244 unless (close $fh) {
245 Xchat::print("${B}FILTER:${B} Couldn't close file to load filter: $!\n");
246 return;
249 Xchat::print("${B}FILTER DENY ----------${B}\n");
250 for (my $i = 0; $i <= $#filter_deny; $i++) {
251 Xchat::print(" [$i]: /".$filter_deny[$i]."/\n");
253 Xchat::print("${B}FILTER DENY ----------${B}\n");
256 sub add_rule ( $ ) {
257 my $rule = shift;
259 # always ading rules at the end
260 push @filter_deny, $rule;
263 sub delete_rule ( $ ) {
264 my $num = shift || $#filter_deny;
266 splice @filter_deny, $num, 1;
269 # ============================================================
270 # ------------------------------------------------------------
271 # ............................................................
273 sub cmd_version {
274 Xchat::print("${B}$scriptName $scriptVersion${B}\n");
275 Xchat::print(" * URL: http://github.com/jnareb/softsnow-xchat2-filter\n");
276 Xchat::print(" * URL: http://gitorious.org/projects/softsnow-xchat2-filter\n");
277 Xchat::print(" * URL: http://repo.or.cz/w/softsnow_xchat2_filter.git\n");
280 sub cmd_status {
281 my $server = shift;
283 if ($filter_turned_on) {
284 Xchat::print("Filter is turned ${B}ON${B}\n");
285 } else {
286 Xchat::print("Filter is turned ${B}OFF${B}\n");
288 if ($limit_to_server) {
289 if ($server eq $limit_to_server) {
290 Xchat::print("Filter is limited to ${B}current${B} ".
291 "server $limit_to_server\n");
292 } else {
293 Xchat::print("Filter is limited to server ".
294 "$limit_to_server != $server\n");
297 if ($use_filter_allow) {
298 Xchat::print("Filter is using ALLOW rules (before DENY)\n");
302 sub cmd_debug {
303 Xchat::print("${B}FILTER DEBUG ----------${B}\n");
304 Xchat::print("Channel: ".Xchat::get_info("channel")."\n");
305 Xchat::print("Host: ".Xchat::get_info("host")."\n");
306 Xchat::print("Server: ".Xchat::get_info("server")."\n");
307 Xchat::print("Server Id: ".Xchat::get_info("id")."\n");
308 Xchat::print("Network: ".Xchat::get_info("network")."\n");
309 Xchat::print("\n");
310 Xchat::printf("%3u %s rules\n", scalar(@filter_allow), "allow");
311 Xchat::printf("%3u %s rules\n", scalar(@filter_deny), "deny");
312 Xchat::print("${B}FILTER DEBUG ----------${B}\n");
315 sub cmd_server_limit {
316 my $server = shift;
318 if ($server) {
319 # adding limiting to given (single) server
320 if ($limit_to_server) {
321 Xchat::print("${B}FILTER:${B} Changing server from $limit_to_server to $server\n");
322 } else {
323 Xchat::print("${B}FILTER:${B} Limiting filtering to server $server\n");
325 $limit_to_server = $server;
327 } else {
328 # removing limiting to server
329 if ($limit_to_server) {
330 Xchat::print("Filter: Removing limit to server $limit_to_server\n");
332 $limit_to_server = '';
337 sub cmd_print_rules {
338 Xchat::print("${B}FILTER PRINT ----------${B}\n");
339 Xchat::print("${B}ALLOW${B}".($use_filter_allow ? ' (on)' : ' (off)')."\n");
341 for (my $i = 0; $i <= $#filter_allow; $i++) {
342 Xchat::print("[$i]: /".$filter_allow[$i]."/\n");
344 Xchat::print("${B}DENY${B}\n");
345 for (my $i = 0; $i <= $#filter_deny; $i++) {
346 Xchat::print("[$i]: /".$filter_deny[$i]."/\n");
348 Xchat::print("${B}FILTER PRINT ----------${B}\n");
351 sub cmd_add_rule {
352 my $rule = shift;
354 if ($rule) {
355 add_rule($rule);
356 Xchat::print("${B}FILTER RULE [$#filter_deny]:${B} /$rule/\n");
357 } else {
358 Xchat::print("Syntax: ${B}/FILTER ADD ${U}rule${U}${B} to add\n")
362 sub cmd_delete_rule {
363 my $num = shift;
365 # strip whitespace
366 $num =~ s/^\s*(.*?)\s*$/$1/g if $num;
367 SWITCH: {
368 unless ($num) {
369 Xchat::print("${B}FILTER:${B} deleting /".$filter_deny[-1]."/\n");
370 $#filter_deny--;
371 Xchat::print("${B}FILTER:${B} deleted successfully last rule\n");
372 last SWITCH;
374 if ($num !~ /^\d+$/) {
375 Xchat::print("${B}FILTER:${B} $num is not a number\n");
376 last SWITCH;
378 if ($num < 0 || $num > $#filter_deny) {
379 Xchat::print("${B}FILTER:${B} $num outside range [0,$#filter_deny]\n");
380 last SWITCH;
382 # default
384 Xchat::print("${B}FILTER:${B} deleting /".$filter_deny[$num]."/\n");
385 delete_rule($num);
386 Xchat::print("${B}FILTER:${B} deleted successfully rule $num\n");
391 sub cmd_show_rule {
392 my $num = shift;
394 $num =~ s/^\s*(.*?)\s*$/$1/g if $num;
396 if (defined $num && $num !~ /^\d+$/) {
397 Xchat::print("${B}FILTER:${B} $num is not a number\n");
398 } elsif (defined $num && !defined $filter_deny[$num]) {
399 Xchat::print("${B}FILTER:${B} rule $num does not exist\n");
400 } else {
401 Xchat::print("${B}FILTER:${B} ".(defined $num ? "[$num]" : "last").
402 " rule /".$filter_deny[defined $num ? $num : -1]."/\n");
406 # ============================================================
407 # ============================================================
408 # ============================================================
410 sub filter_command_handler {
411 my $cmd = $_[0][1]; # 1st parameter (after FILTER)
412 my $arg = $_[1][2]; # 2nd word to the last word
413 my $server = Xchat::get_info("host");
416 if (!$cmd || $cmd =~ /^STATUS$/i) {
417 cmd_status($server);
419 } elsif ($cmd =~ /^ON$/i) {
420 $filter_turned_on = 1;
421 Xchat::print("Filter turned ${B}ON${B}\n");
423 } elsif ($cmd =~ /^OFF$/i) {
424 $filter_turned_on = 0;
425 Xchat::print("Filter turned ${B}OFF${B}\n");
427 } elsif ($cmd =~ /^SERVER$/i) {
428 cmd_server_limit($server);
430 } elsif ($cmd =~ /^SERVERON$/i) {
431 cmd_server_limit($server);
433 $filter_turned_on = 1;
434 Xchat::print("Filter turned ${B}ON${B}\n");
436 } elsif ($cmd =~ /^ALL$/i) {
437 cmd_server_limit(undef);
439 } elsif ($cmd =~ /^HELP$/i) {
440 Xchat::print($filter_help);
441 Xchat::print($filterwindow_help);
443 } elsif ($cmd =~ /^VERSION$/i) {
444 cmd_version();
446 } elsif ($cmd =~ /^DEBUG$/i || $cmd =~ /^INFO$/i) {
447 cmd_debug();
449 } elsif ($cmd =~ /^(?:PRINT|LIST)$/i) {
450 cmd_print_rules();
452 } elsif ($cmd =~ /^ALLOW$/i) {
453 $use_filter_allow = !$use_filter_allow;
454 Xchat::print("${B}FILTER:${B} ALLOW rules ".
455 ($use_filter_allow ? "enabled" : "disabled")."\n");
457 } elsif ($cmd =~ /^ADD$/i) {
458 cmd_add_rule($arg);
460 } elsif ($cmd =~ /^DEL(?:ETE)$/i) {
461 cmd_delete_rule($arg);
463 } elsif ($cmd =~ /^SHOW$/i) {
464 cmd_show_rule($arg);
466 } elsif ($cmd =~ /^SAVE$/i) {
467 save_filter();
468 Xchat::print("${B}FILTER:${B} saved DENY rules to $filter_file\n");
470 } elsif ($cmd =~ /^(RE)?LOAD$/i) {
471 load_filter();
472 Xchat::print("${B}FILTER:${B} loaded DENY rules from $filter_file\n");
474 } else {
475 Xchat::print("Unknown command ${B}/FILTER $_[1][1]${B}\n") if $cmd;
477 return 1;
480 sub filterwindow_command_handler {
481 my $cmd = $_[0][1]; # 1st parameter (after FILTER)
482 #my $arg = $_[1][2]; # 2nd word to the last word
483 my $ctx = Xchat::find_context($filter_window);
485 if (!$cmd || $cmd =~ /^STATUS$/i) {
486 Xchat::print(($filtered_to_window ? "Show" : "Don't show").
487 " filtered content in ".
488 (defined $ctx ? "open" : "closed").
489 " window ${B}$filter_window${B}\n");
491 } elsif ($cmd =~ /^DEBUG$/i) {
492 my $ctx_info = Xchat::context_info($ctx);
493 Xchat::print("${B}FILTERWINDOW DEBUG ----------${B}\n");
494 Xchat::print("filtered_to_window = $filtered_to_window\n");
495 Xchat::print("filter_window = $filter_window\n");
496 if (defined $ctx) {
497 Xchat::print("$filter_window is ${B}open${B}\n");
498 Xchat::print("$filter_window: network => $ctx_info->{network}\n")
499 if defined $ctx_info->{'network'};
500 Xchat::print("$filter_window: host => $ctx_info->{host}\n")
501 if defined $ctx_info->{'host'};
502 Xchat::print("$filter_window: channel => $ctx_info->{channel}\n");
503 Xchat::print("$filter_window: server_id => $ctx_info->{id}\n");
504 if defined $ctx_info->{'id'};
505 } else {
506 Xchat::print("$filter_window is ${B}closed${B}\n");
508 # requires XChat >= 2.8.2
509 #Xchat::print("'Channel Message' format: ".
510 # Xchat::get_info("event_text Channel Message")."\n");
511 #Xchat::print("'Channel Msg Hilight' format: ".
512 # Xchat::get_info("event_text Channel Msg Hilight")."\n");
513 Xchat::print("${B}FILTERWINDOW DEBUG ----------${B}\n");
515 } elsif ($cmd =~ /^ON$/i) {
516 Xchat::command("QUERY $filter_window");
517 Xchat::print("${B}----- START LOGGING FILTERED CONTENTS -----${B}\n",
518 $filter_window)
519 if !$filtered_to_window;
521 $filtered_to_window = 1;
522 Xchat::print("Show filtered content in ${B}$filter_window${B}\n");
524 } elsif ($cmd =~ /^OFF$/i) {
525 Xchat::print("${B}----- STOP LOGGING FILTERED CONTENTS -----${B}\n",
526 $filter_window)
527 if $filtered_to_window;
528 #Xchat::command("CLOSE", $FilterWindow);
530 $filtered_to_window = 0;
531 Xchat::print("Don't show filtered content in ${B}$filter_window${B}\n");
533 } elsif ($cmd =~ /^HELP$/i) {
534 Xchat::print($filterwindow_help);
536 } else {
537 Xchat::print("Unknown command ${B}/FILTERWINDOW $_[1][1]${B}\n") if $cmd;
538 Xchat::print("${B}${U}USAGE:${U} /FILTERWINDOW ON|OFF${B}\n");
541 return 1;
544 # ======================================================================
545 # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
546 # ----------------------------------------------------------------------
548 Xchat::print("${B}$scriptName $scriptVersion${B} loaded\n");