cleanicks.pl


   1 #!/usr/bin/perl
   2 # created for irssi 0.8.6, Copyright (c) 2003 Guillaume SMET
   3 
   4 # TODO
   5 #
   6 # - travailler avec des paramètres IRSSI -> en cours, mais lesquels? -> à voir à l'usage
   7 # - retravailler cut_nick avec une regexp : done
   8 # - filtrer les quit et les join, voire passer en param le level des messages à filtrer
   9 # - filtrer les nicks lors du changement de nick même qd les cleanicks sont différents : ?
  10 # - faire un hash pour les NICK_WARN_* et passer en param ?
  11 #
  12 # - documenter un peu le code
  13 #
  14 # - commande d'aide
  15 # - commande qui affiche la configuration actuelle -> /set cleanicks
  16 #
  17 
  18 use strict;
  19 use Irssi 20020324;
  20 use Irssi::Irc;
  21 use vars qw($VERSION %IRSSI);
  22 $VERSION = "0.08";
  23 %IRSSI   = (
  24     authors     => "Guillaume SMET, Mathieu DOIDY, Thomas ROUSSEL",
  25     contact     => "",
  26     name        => "Cleanicks",
  27     description => "Cut nicks when users bring their lives onto the channel (nick[eating]->nick[sleeping]->...) and redirect nick changes to wherever you want",
  28     license     => "GNU GPL v2",
  29     url         => "http://irssi.roulaize.net",
  30     changed     => "24/09/2003"
  31 );
  32 
  33 # ignore nick changes
  34 use constant NICK_WARN_IGNORE => 'ignore';
  35 
  36 # put nick changes in the cleanicks_window window without hl
  37 use constant NICK_WARN_WINDOW_QUIET => 'window quiet';
  38 
  39 # put nick changes in the cleanicks_window window with hl
  40 use constant NICK_WARN_WINDOW => 'window';
  41 
  42 # keep the standard behaviour
  43 use constant NICK_WARN_KEEP => 'keep';
  44 
  45 # this regexp is used to match (nick): foo bar
  46 our $nickRegexp = '[^ :]*';
  47 
  48 # separators used for the nicks
  49 our $separatorRegexpClass = '\\\`_\[\]{}\-|';
  50 
  51 # channels which cleanicks is active on
  52 Irssi::settings_add_str( $IRSSI{name}, 'cleanicks_channels', '' );
  53 
  54 # list of nicks pattern to replace, it should be a space separated string of pattern[>replacement]
  55 Irssi::settings_add_str( $IRSSI{name}, 'cleanicks_nicks', '' );
  56 
  57 # name of the window where nick changes are redirected to
  58 Irssi::settings_add_str( $IRSSI{name}, 'cleanicks_window', 'changeNick' );
  59 
  60 # nicks are not cut before min_length
  61 Irssi::settings_add_int( $IRSSI{name}, 'cleanicks_min_length', 3 );
  62 
  63 # avoid too long nickname, cutting them to max length
  64 Irssi::settings_add_int( $IRSSI{name}, 'cleanicks_max_length', 14 );
  65 
  66 # when turned on, only nick <-> nick* changes are filtered, when turned off, every change is filtered
  67 Irssi::settings_add_bool( $IRSSI{name}, 'cleanicks_advanced_filtering', 1 );
  68 
  69 # 0 : filter every nick change / 1 : advanced filtering
  70 our $cleanicks_nickWarnMode = NICK_WARN_WINDOW_QUIET;
  71 
  72 Irssi::theme_register( [ 'nick_changed_channel', '{channick $0} is now known as {channick_hilight $1}', 'nick_changed_window', '%W$2%n {channick $0} is now known as {channick_hilight $1}' ] );
  73 
  74 sub sig_channel_event {
  75     my ( $signal, $chanIndex, $nickIndex, $msgIndex, $modeIndex, $msgType ) = @_;
  76     my ( $nick, $channel, $newNickRec );
  77     if ( $chanIndex > -1 && _is_channel_filtered( $signal->[$chanIndex] ) ) {
  78         $nick = $signal->[$nickIndex];
  79         $signal->[$nickIndex] = _cut_nick( $signal->[$nickIndex] ) if $nickIndex > -1;
  80         $signal->[$msgIndex] = _clean_message( $signal->[$msgIndex], $signal->[0], $signal->[$chanIndex] ) if $msgIndex > -1 && $chanIndex > -1;
  81         if ($modeIndex) { # it is a mode change
  82             my @mnicks = split( ' ', $signal->[$modeIndex] );
  83             $signal->[$modeIndex] = (shift @mnicks). ' ' . join (' ', map( { _cut_nick($_) } @mnicks));
  84         }
  85     }
  86     if (!defined($chanIndex) && $nickIndex) { # it is a quit msg
  87        my ($i, @added_nick_in_chan );
  88        foreach ( split( ' ', Irssi::settings_get_str('cleanicks_channels') ) ) {
  89            my $channel = $signal->[0]->channel_find($_);
  90            next unless defined($channel);
  91            my $nick = $channel->nick_find($signal->[$nickIndex]);
  92            next unless defined($nick);
  93            $_ = $nick;
  94            $channel->nick_remove ($_);
  95            $signal->[$nickIndex] = _cut_nick( $signal->[$nickIndex] );
  96            $newNickRec = $channel->nick_insert( $signal->[$nickIndex], $_->{op}, $_->{halfop}, $_->{voice}, 0 );
  97            $added_nick_in_chan[$i++]=$channel;
  98        }
  99        Irssi::signal_continue(@$signal);
 100        map { if ($_->nick_find($signal->[$nickIndex])) { 
 101                      $_->nick_remove($signal->[$nickIndex]) 
 102              } 
 103            } @added_nick_in_chan;
 104        return;
 105     }
 106     if ($nickIndex > -1 && $chanIndex > -1 && $signal->[$nickIndex] ne $nick & _is_channel_filtered($signal->[$chanIndex])) {
 107         # from tracknick.pl http://irssi.org/scripts/html/tracknick.pl.html (just added the halfop parameter)
 108         $channel = $signal->[0]->channel_find( $signal->[$chanIndex] );
 109 
 110         $_ = $channel->nick_find($nick);
 111         $newNickRec = $channel->nick_insert( $signal->[$nickIndex], $_->{op}, $_->{halfop}, $_->{voice}, 0 );
 112         Irssi::signal_continue(@$signal);
 113         $channel->nick_remove($newNickRec);
 114         return;
 115     } 
 116     Irssi::signal_continue(@$signal);
 117 }
 118 
 119 sub sig_nick {
 120     my ( $server, $newNick, $oldNick, $address ) = @_;
 121 
 122     if ( $cleanicks_nickWarnMode ne NICK_WARN_KEEP ) {
 123 
 124         my @channels                     = split( ' ', Irssi::settings_get_str('cleanicks_channels') );
 125         my $cleanicks_window             = Irssi::settings_get_str('cleanicks_window');
 126         my $cleanicks_advanced_filtering = Irssi::settings_get_bool('cleanicks_advanced_filtering');
 127         my $advancedFilter               = 1;
 128 
 129         my ( $cutOldNick, $cutNewNick, $length, $filteredChannelCount, $channelList );
 130 
 131         if ($cleanicks_advanced_filtering) {
 132             $cutOldNick = _cut_nick($oldNick);
 133             $cutNewNick = _cut_nick($newNick);
 134             $length     = (
 135                   length($cutOldNick) < length($cutNewNick)
 136                 ? length($cutOldNick)
 137                 : length($cutNewNick)
 138             );
 139             $advancedFilter = ( lc( substr( $cutOldNick, 0, $length ) ) eq lc( substr( $cutNewNick, 0, $length ) ) ? 1 : 0 );
 140         }
 141 
 142         foreach my $channel ( $server->channels() ) {
 143             my ( $msgChannel, $filter );
 144             my $channelName = $channel->{name};
 145 
 146             foreach ( $channel->nicks() ) {
 147                 next unless $_->{nick} eq $newNick;
 148                 $msgChannel = 1;
 149                 last;
 150             }
 151 
 152             if ($msgChannel) {
 153                 $filter = _is_channel_filtered($channelName);
 154 
 155                 if ( !$filter || !$advancedFilter ) {
 156                     $channel->printformat( MSGLEVEL_NICKS, 'nick_changed_channel', $oldNick, $newNick );
 157                 }
 158                 elsif ( $cleanicks_nickWarnMode ne NICK_WARN_IGNORE ) {
 159                     $channelList .= ', ' if $filteredChannelCount++;
 160                     $channelList .= $channelName;
 161                 }
 162             }
 163         }
 164         if ($filteredChannelCount) {
 165             my $window = Irssi::window_find_name($cleanicks_window);
 166 
 167             if ( !$window ) {
 168                 $window = Irssi::Windowitem::window_create( $cleanicks_window, 1 );
 169                 $window->set_name($cleanicks_window);
 170                 $window->print( 'This window was created by ' . $IRSSI{name}, MSGLEVEL_NICKS );
 171             }
 172             if ( $cleanicks_nickWarnMode eq NICK_WARN_WINDOW ) {
 173                 $window->printformat( MSGLEVEL_NICKS, 'nick_changed_window', $oldNick, $newNick, $channelList );
 174             }
 175             elsif ( $cleanicks_nickWarnMode eq NICK_WARN_WINDOW_QUIET ) {
 176                 $window->printformat( MSGLEVEL_NO_ACT | MSGLEVEL_NICKS, 'nick_changed_window', $oldNick, $newNick, $channelList );
 177             }
 178         }
 179         Irssi::signal_stop();
 180     }
 181 }
 182 
 183 sub _clean_message {
 184     my ($msg, $server, $channel) = @_;
 185     if ( $channel && $msg =~ /^($nickRegexp)([: ]+.*)$/ ) {
 186         foreach ( $server->channel_find($channel)->nicks() ) {
 187             next unless $_->{nick} eq $1;
 188             $msg = _cut_nick($1) . $2;
 189             last;
 190         }
 191     }
 192     return $msg;
 193 }
 194 
 195 sub _cut_nick {
 196     my $nick = shift ;
 197     return $nick if $nick =~ /[@.]/; # this is not a nick but a hostmask or a server
 198 
 199     my $cleanicks_min_length = Irssi::settings_get_int('cleanicks_min_length');
 200     my $cleanicks_max_length = Irssi::settings_get_int('cleanicks_max_length');
 201 
 202     $nick = substr( $nick, 0, $cleanicks_max_length );
 203 
 204     ### my $nickre = '^[' . $separatorRegexpClass . ']*([^' . $separatorRegexpClass . '].{' . ( $cleanicks_min_length - 1 ) . '}[^' . $separatorRegexpClass . ']*)[' . $separatorRegexpClass . ']?.*';
 205 
 206     my $nickre = '^[^[:alnum:]]*([[:alnum:]].{'.($cleanicks_min_length - 1).'}[[:alnum:]]*)[^[:alnum:]]?.*$';
 207 
 208     $nick =~ s/$nickre/\1/ ;
 209 
 210     foreach ( split( ' ', Irssi::settings_get_str('cleanicks_nicks') ) ) {
 211         my ( $pattern, $fake ) = split( '>', $_ );
 212         $fake = $fake ? $fake : $pattern;
 213         $nick = $fake if $nick =~ /$pattern/;
 214     }
 215 
 216     return $nick;
 217 }
 218 
 219 sub _is_channel_filtered {
 220     my $channel = shift;
 221     my @channels = split( ' ', Irssi::settings_get_str('cleanicks_channels') );
 222     foreach (@channels) {
 223         next unless $channel eq $_;
 224         return 1;
 225     }
 226     return 0;
 227 }
 228 
 229 sub cmd_addchan {
 230     my $chanlist = Irssi::settings_get_str('cleanicks_channels').' '.(shift);
 231     Irssi::settings_set_str( 'cleanicks_channels' => $chanlist );
 232 }
 233 
 234 sub cmd_addnick {
 235     my $nicklist = Irssi::settings_get_str('cleanicks_nicks').' '.(shift);
 236     Irssi::settings_set_str( 'cleanicks_nicks' => $nicklist );
 237 }
 238 
 239 Irssi::command_bind('cleanicks addchannel',\&cmd_addchan);
 240 Irssi::command_bind('cleanicks addnick',\&cmd_addnick);
 241 # style ripped from coekie's trigger.pl
 242 Irssi::command_bind 'cleanicks' => sub {
 243     my ( $data, $server, $item ) = @_;
 244     $data =~ s/\s+$//g;
 245     Irssi::command_runsub ( 'cleanicks', $data, $server, $item ) ;
 246 };
 247 
 248 Irssi::signal_add_last( 'message public',     sub { sig_channel_event( \@_, 4, 2,  1 ) } );
 249 Irssi::signal_add_last( 'message own_public', sub { sig_channel_event( \@_, 2, -1, 1 ) } );
 250 Irssi::signal_add_last( 'message irc action', sub { sig_channel_event( \@_, 4, 2,  -1 ) } );
 251 Irssi::signal_add_last( 'message kick',       sub { sig_channel_event( \@_, 1, 2,  -1 ) } );
 252 Irssi::signal_add_last( 'message topic',      sub { sig_channel_event( \@_, 1, 3,  -1 ) } );
 253 Irssi::signal_add( 'message irc mode', sub { sig_channel_event( \@_, 1, 2, -1, 4 ) } );
 254 Irssi::signal_add_last( 'message nick', 'sig_nick' );
 255 Irssi::signal_add_last( 'message join', sub { sig_channel_event( \@_, 1, 2, -1 ) } );
 256 Irssi::signal_add_last( 'message part', sub { sig_channel_event( \@_, 1, 2, -1 ) } );
 257 
 258 ### the handling of quit messages is a bit buggy 
 259 ### Irssi::signal_add_first( 'message quit', sub { sig_channel_event( \@_, undef, 1, -1 ) } );
 260 ### Irssi::signal_add_first( 'event quit', sub { sig_channel_event( \@_, undef, 2, -1 ) } );
 261 
 262 print CLIENTCRAP '%B>>%n ' . $IRSSI{name} . ' ' . $VERSION . ' loaded';