html/modelist-r.pl


   1 # $Id: modelist-r.pl,v 0.8.0-rc4 2004/11/04 19:56 derwan Exp $
   2 #
   3 # This script creates cache of channel invites, ban exceptions and reops.
   4 # Reop list is included only in ircd >= 2.11.0 (in IRCnet) - for other servers
   5 # and networks use modelist.pl ( http://derwan.irssi.pl/modelist.pl).
   6 #
   7 # Script commands:
   8 #   /si   - shows channel invites
   9 #   /se   - shows ban exception
  10 #   /sr   - shows reop list
  11 #   
  12 #   /uninvite [<index and masks separated with spaces>]
  13 #         - removes the specified invite(s) from the channel
  14 #   /unexcept [<index and masks separated with spaces>]
  15 #         - removes the specified ban exception(s) from the channel
  16 #   /unreop [<index and masks separated with spaces>]
  17 #         - removes the specified reop(s) from the channel
  18 #
  19 #   Examples:
  20 #      /si
  21 #      /uninvite 1
  22 #      /unexcept *!*@127.0.0.1
  23 #      /unreop 1 *!*@127.0.0.1 5
  24 #
  25 # After loading modelist-r.pl run command
  26 #   /statusbar window add -priority 0 -after usercount modelist 
  27 #
  28 # You can customize the look of this item from theme file:
  29 #   sb_modelist = "{sb $0 modes ($1-)}";
  30 #   sb_ml_b = "b/%r$*%n"; # bans
  31 #   sb_ml_e = "e/%c$*%n"; # ban exceptions
  32 #   sb_ml_I = "I/%G$*%n"; # invites
  33 #   sb_ml_R = "R/%R$*%n"; # reops
  34 #   sb_ml_space = " ";    # separator
  35 #
  36 # Theme formats:
  37 #   modelist                   $0 - index, $1 - channel, $2 - hostmask, 3 - mode 
  38 #   modelist_long              $4 - nick, $5 - time
  39 #   modelist_empty             $0 - channel, $1 - mode
  40 #   modelist_chan_not_synced   $0 - channel
  41 #   modelist_not_joined
  42 #   modelist_server_version    $0 - version
  43 #   
  44 
  45 use strict;
  46 use vars ('$VERSION', '%IRSSI');
  47 
  48 use Irssi 20020600 ();
  49 use Irssi::Irc;
  50 use Irssi::TextUI;
  51 
  52 $VERSION = '0.8.0-rc4';
  53 %IRSSI =
  54 (
  55    'authors'      => 'Marcin Rozycki',
  56    'contact'      => 'derwan@irssi.pl',
  57    'name'         => 'modelist-r',
  58    'description'  => 'Cache of invites, ban exceptions and reops in channel. Script commands: '.
  59                      '/si, /se, /sr, /unexcept, /uninvite, /unreop (version only for ircd >= 2.11.0).',
  60    'license'      => 'GNU GPL v2',
  61    'modules'      => '',
  62    'url'          => 'http://derwan.irssi.pl',
  63    'changed'      => 'Thu Nov  4 17:56:17 2004',
  64 );
  65 
  66 Irssi::theme_register
  67 ([
  68    # $0 - index, $1 - channel name, $2 - hostmask, $3 - mode (invite, ban exception, reop)
  69    'modelist', '$0 - {channel $1}: $3 {ban $2}',
  70    # $0 - index, $1 - channel name, $2 - hostmask, $3 - mode, $4 - nick, $5 - time
  71    'modelist_long', '$0 - {channel $1}: $3 {ban $2} {comment by {nick $4}, $5 secs ago}',
  72    # $0 - channel name, $1 - mode
  73    'modelist_empty', 'No $1s in channel {channel $0}',
  74    # $0 - channel name
  75    'modelist_chan_not_synced', 'Channel not fully synchronized yet, try again after a while',
  76    # $0 - channel name
  77    'modelist_chan_no_modes', 'Channel {channel $0} doesn\'t support modes',
  78    'modelist_not_joined', 'Not joined to any channel',
  79    # $0 - version
  80    'modelist_server_version', 'This script working only in ircd {hilight >= 2.11.0} with reop list {comment active ircd $0}' 
  81 ]);
  82 
  83 # $modelist{str servertag}->{lc str channel}->{str mode} = [ $moderec, ... ]
  84 # $moderec = [ str hostmask, str nick, str time ]
  85 my %modelist = ();
  86 
  87 # $synced{str servertag}->{lc str channel} = int synced
  88 my %synced = ();
  89 
  90 #  $visible{str mode} = str list
  91 my %visible =
  92 (
  93    'e' => 'ban exception',
  94    'I' => 'invite',
  95    'R' => 'reop'
  96 );
  97 
  98 # $sb->{str mode} = int modes
  99 my $sb = {};
 100 
 101 # server redirections:
 102 #   'modelist I' ( 346, 347, 403, 442, 472, 479, 482)
 103 #   'modelist e' ( 348, 349, 403, 442, 472, 479, 482)
 104 #   'modelist R' ( 344, 345, 403, 442, 472, 479, 482)
 105 Irssi::Irc::Server::redirect_register('modelist I', 0, 0, { 'event 346' => 1 }, {
 106    'event 347' => 1, # end of channel invite list
 107    'event 403' => 1, # no such channel
 108    'event 442' => 1, # you're not on that channel
 109    'event 472' => 1, # unknown mode
 110    'event 479' => 1, # illegal channel name
 111    'event 482' => 1  # you're not channel operator
 112 }, undef );
 113 
 114 Irssi::Irc::Server::redirect_register('modelist e', 0, 0, { 'event 348' => 1 }, {
 115    'event 349' => 1, # end of channel exception list
 116    'event 403' => 1,
 117    'event 442' => 1,
 118    'event 472' => 1,
 119    'event 479' => 1,
 120    'event 482' => 1
 121 }, undef );
 122 
 123 Irssi::Irc::Server::redirect_register('modelist R', 0, 0, { 'event 344' => 1 }, {
 124    'event 345' => 1, # end of channel reop list
 125    'event 403' => 1,
 126    'event 442' => 1,
 127    'event 472' => 1,
 128    'event 479' => 1,
 129    'event 482' => 1
 130 }, undef );
 131 
 132 # create_channel (rec channel, int sync)
 133 sub create_channel ($;$)
 134 {
 135     destroy_channel($_[0]);
 136     sb_update();
 137 
 138     my ($server, $tag, $channel) = ($_[0]->{server}, $_[0]->{server}->{tag}, lc $_[0]->{name});
 139     
 140     
 141     if ( !test_version($server) or $_[0]->{no_modes} )
 142     {
 143        $synced{$tag}->{$channel} = 1;
 144        return;
 145     }
 146     $synced{$tag}->{$channel} = ( defined $_[1] ) ? $_[1] : 0;
 147         
 148     $modelist{$tag}->{$channel}->{I} = [];
 149     $server->redirect_event('modelist I', 1, $channel, 0, undef, {
 150        'event 346' => 'redir modelist invite',
 151                 '' => 'event empty'
 152     });
 153     $server->send_raw(sprintf('mode %s +I', $channel));
 154 
 155     $modelist{$tag}->{$channel}->{e} = [];
 156     $server->redirect_event('modelist e', 1, $channel, 0, undef, {
 157        'event 348' => 'redir modelist except',
 158                 '' => 'event empty'
 159     });
 160     $server->send_raw(sprintf('mode %s +e', $channel));
 161 
 162     $modelist{$tag}->{$channel}->{R} = [];
 163     $server->redirect_event('modelist R', 1, $channel, 0, undef, {
 164        'event 344' => 'redir modelist reop',
 165        'event 345' => 'redir modelist sync',
 166        'event 403' => 'redir modelist sync',
 167        'event 442' => 'redir modelist sync',
 168        'event 472' => 'redir modelist sync',
 169        'event 479' => 'redir modelist sync',
 170        'event 482' => 'redir modelist sync',
 171        '' => 'event empty'
 172     });
 173     $server->send_raw(sprintf('mode %s +R', $channel));
 174 }
 175 
 176 # destroy_channel (rec channel)
 177 sub destroy_channel ($)
 178 {
 179    my ($tag, $channel) = ($_[0]->{server}->{tag}, lc $_[0]->{name});
 180    delete $synced{$tag}->{$channel};
 181    delete $modelist{$tag}->{$channel};
 182    sb_update();
 183 }
 184 
 185 # sig_redir_modelist (rec server, str data, str mode)
 186 sub sig_redir_modelist ($$$)
 187 {
 188    my $chanrec = $_[0]->channel_find(((split(' ', $_[1], 3))[1]));
 189    if ( ref $chanrec )
 190    {
 191       mode($chanrec, 1, $_[2], ((split(/ +/, $_[1], 4))[2]), undef);
 192    }
 193 }
 194 
 195 # mode (rec channel, int type, str mode, str hostmask, str setby) 
 196 sub mode ($$$$$)
 197 {
 198     my $rec = get_list($_[0], $_[2]);
 199     if ( ref $rec and $_[1] eq 1 )
 200     {    
 201        push @{$rec}, [ $_[3], $_[4], time ];
 202     }
 203     elsif ( ref $rec and $_[1] eq 0 )
 204     {
 205        for ( my $idx = 0; $idx <= $#{$rec}; $idx++ )
 206        {
 207          if ( lc $rec->[$idx]->[0] eq lc $_[3] )
 208          {
 209             splice @{$rec}, $idx, 1;
 210             last;
 211          }
 212        }
 213     }
 214     sb_update();
 215 }
 216 
 217 # sig_channel_sync (rec channel)
 218 sub sig_channel_sync ($)
 219 {
 220    if ( ++$synced{$_[0]->{server}->{tag}}->{lc $_[0]->{name}} < 2 )
 221    {
 222       Irssi::signal_stop();
 223    }
 224 }
 225 
 226 # sig_modelist_sync (rec server, str data)
 227 sub sig_modelist_sync ($$)
 228 {
 229    my $chanrec = $_[0]->channel_find(((split(/ +/, $_[1], 3))[1]));
 230    if ( ref $chanrec )
 231    {
 232       Irssi::signal_emit('channel sync', $chanrec);
 233       sb_update();
 234    }
 235 }
 236 
 237 # sig_message_irc_mode (rec server, str channel, str nick, str userhost, str mode)
 238 sub sig_message_irc_mode ($$$$$)
 239 {
 240    my $chanrec = $_[0]->channel_find($_[1]);
 241    unless ( ref $chanrec )
 242    {
 243       return;
 244    }
 245    
 246    my ($q, $mods, @a) = (1, split(/ +/, $_[4]));
 247    foreach my $mod ( split('', $mods) )
 248    {
 249        ( $mod eq '+' ) and $q = 1, next;
 250        ( $mod eq '-' ) and $q = 0, next;
 251        my $a = ( rindex('beIkloRvhx', $mod) >= 0 && $q eq 1 or rindex('beIkoRvhx', $mod) >= 0 && $q eq 0 ) ? shift(@a) : undef;
 252        if ( rindex('eIR', $mod) >= 0 )
 253        {
 254           mode($chanrec, $q, $mod, $a, $_[2]);
 255        }
 256    }
 257 }
 258 
 259 # get_list (rec channel, str mode), rec list
 260 sub get_list ($$)
 261 {
 262    if ( ref $_[0] and defined $modelist{$_[0]->{server}->{tag}}->{lc $_[0]->{name}}->{$_[1]} )
 263    {
 264        return $modelist{$_[0]->{server}->{tag}}->{lc $_[0]->{name}}->{$_[1]};
 265    }
 266 }
 267 
 268 # test_version (rec server), bool 0/1
 269 sub test_version ($)
 270 {
 271    if ( $_[0] and ref $_[0] and $_[0]->{version} =~ m/^(\d+\.\d+)\./ and $1 >= 2.11 )
 272    {
 273       return 1;
 274    }
 275    return 0;
 276 }
 277 
 278 
 279 # test_channel (rec channel, bool quiet), bool 0/1
 280 sub test_channel ($;$)
 281 {
 282    unless ( ref $_[0] and $_[0]->{type} eq 'CHANNEL' )
 283    {
 284        Irssi::printformat(MSGLEVEL_CRAP, 'modelist_not_joined') unless ( $_[1] );
 285        return 0;
 286    }
 287    if ( $_[0]->{no_modes} )
 288    {
 289        $_[0]->printformat(MSGLEVEL_CRAP, 'modelist_chan_no_modes', $_[0]->{name}) unless ( $_[1] );
 290        return 0;
 291    }
 292    if ( !test_version($_[0]->{server}) )
 293    {
 294       $_[0]->printformat(MSGLEVEL_CRAP, 'modelist_server_version', $_[0]->{server}->{version}) unless ( $_[1] );
 295       return 0;
 296    
 297    }
 298    if ( $synced{$_[0]->{server}->{tag}}->{lc $_[0]->{name}} < 2 )
 299    {
 300       $_[0]->printformat(MSGLEVEL_CRAP, 'modelist_chan_not_synced', $_[0]->{name}) unless ( $_[1] );
 301       return 0;
 302    }   
 303    return 1;
 304 }
 305 
 306 # cmd_modelist_show (str mode)
 307 sub cmd_modelist_show ($)
 308 {
 309    my $chanrec = Irssi::active_win() ? Irssi::active_win()->{active} : undef;
 310    unless ( test_channel($chanrec) )
 311    {
 312       return;
 313    }
 314    my $rec = get_list($chanrec, $_[0]);
 315    unless ( $#{$rec} >= 0 )
 316    {
 317        $chanrec->printformat
 318        (
 319            MSGLEVEL_CRAP, 'modelist_empty', $chanrec->{name}, $visible{$_[0]}
 320        );
 321        return;
 322    }        
 323    for ( my $idx = 0; $idx <= $#{$rec}; $idx++ )
 324    {
 325       $chanrec->printformat
 326       (
 327           MSGLEVEL_CRAP, ( defined $rec->[$idx]->[1] ? 'modelist_long' : 'modelist'), 
 328           ($idx + 1), $chanrec->{name}, visible($rec->[$idx]->[0]), $visible{$_[0]},
 329           $rec->[$idx]->[1], (time() - $rec->[$idx]->[2])
 330        );
 331    }
 332 }
 333 
 334 # cmd_modelist_del (str mode, str data)
 335 sub cmd_modelist_del ($$)
 336 {
 337    my $chanrec = Irssi::active_win() ? Irssi::active_win()->{active} : undef;
 338    unless ( test_channel($chanrec) )
 339    {
 340       return;
 341    }
 342    my ($rec, @m) = (get_list($chanrec, $_[0]));
 343    foreach my $search ( split /[,;\s]+/, $_[1] )
 344    {
 345       if ( $search =~ m/^\d+$/ ) 
 346       {
 347           next unless ( $search-- and $search <= $#{$rec} );
 348           $search = $rec->[$search]->[0];
 349       }
 350       push @m, $search;
 351    }
 352    if ( $#m >= 0 )
 353    {
 354        $chanrec->{server}->command(sprintf("mode %s -%s %s", $chanrec->{name}, $_[0] x scalar(@m), join(' ', @m)));
 355    }
 356 }
 357 
 358 # visible (str data), str data
 359 sub visible ($)
 360 {
 361    my $str = shift();
 362    $str =~ tr/\240\002\003\037\026/\206\202\203\237\226/;
 363    return $str;
 364 }
 365 
 366 # sb_update ()
 367 sub sb_update ()
 368 {
 369    $sb->{b} = $sb->{e} = $sb->{I} = $sb->{R} = $sb->{T} = 0;
 370    
 371    my $chanrec = Irssi::active_win() ? Irssi::active_win()->{active} : undef;
 372    unless ( test_channel($chanrec, 1) )
 373    {
 374       return;
 375    }
 376 
 377    $sb->{b} = scalar @{[$chanrec->bans]};
 378    $sb->{e} = scalar @{get_list($chanrec, 'e')};
 379    $sb->{I} = scalar @{get_list($chanrec, 'I')};
 380    $sb->{R} = scalar @{get_list($chanrec, 'R')};
 381    $sb->{T} = $sb->{b} + $sb->{e} + $sb->{I} + $sb->{R};
 382 
 383    Irssi::statusbar_items_redraw('modelist');
 384 }
 385 
 386 # sb_modelist(rec item, bool get_size_only)
 387 # tahnks usercount.pl!
 388 sub sb_modelist ($$)
 389 {
 390    unless ( $sb->{T} )
 391    {
 392       $_[0]->{min_size} = $_[0]->{max_size} = 0 if ( ref $_[0] );
 393       return;
 394    }
 395 
 396    my $theme = Irssi::current_theme();
 397    my $format = $theme->format_expand('{sb_modelist}');
 398 
 399    if ( $format  )
 400    {
 401       my ($str, $space) = ('', $theme->format_expand('{sb_ml_space}'));
 402       foreach my $mod ( 'b', 'e', 'I', 'R' )
 403       {
 404          next unless ( $sb->{$mod} > 0 );
 405          my $tmp = $theme->format_expand
 406          (
 407              sprintf('{sb_ml_%s %d}', $mod, $sb->{$mod}), Irssi::EXPAND_FLAG_IGNORE_EMPTY
 408          );
 409          $str .= $tmp . $space;
 410       }
 411       $str =~ s/\Q$space\E$//;
 412       $format = $theme->format_expand
 413       (
 414          sprintf('{sb_modelist %d %s}', $sb->{T}, $str), Irssi::EXPAND_FLAG_IGNORE_REPLACES
 415       );
 416    }
 417    else
 418    {
 419        my $str = undef;
 420        foreach my $mod ( 'b', 'e', 'I', 'R' )
 421        {
 422           next unless ( $sb->{$mod} > 0 );
 423           $str .= sprintf('%s%d ', $mod, $sb->{$mod})
 424        }
 425        chop($str);
 426        $format = sprintf('{sb \%%_%d\%%_ modes ', $sb->{T});
 427        $format .= sprintf('\%%c(\%%n%s\%%c)', $str) if ( $str );
 428    }  
 429 
 430    $_[0]->default_handler($_[1], $format, undef, 1);
 431 }
 432 
 433 Irssi::signal_add_first('channel sync', 'sig_channel_sync');
 434 Irssi::signal_add('channel joined' => sub { create_channel($_[0], 0) });
 435 Irssi::signal_add('channel destroyed' => sub { destroy_channel($_[0]) });
 436 Irssi::signal_add('redir modelist invite' => sub { sig_redir_modelist($_[0], $_[1], 'I'); });
 437 Irssi::signal_add('redir modelist except' => sub { sig_redir_modelist($_[0], $_[1], 'e'); });
 438 Irssi::signal_add('redir modelist reop' => sub { sig_redir_modelist($_[0], $_[1], 'R'); });
 439 Irssi::signal_add('redir modelist sync', 'sig_modelist_sync');
 440 Irssi::signal_add('message irc mode', 'sig_message_irc_mode');
 441 Irssi::signal_add_last('ban new', 'sb_update');
 442 Irssi::signal_add_last('ban remove', 'sb_update');
 443 Irssi::signal_add_last('window changed', 'sb_update');
 444 Irssi::signal_add_last('window item changed', 'sb_update');
 445 Irssi::command_bind('si' => sub { cmd_modelist_show('I') });
 446 Irssi::command_bind('se' => sub { cmd_modelist_show('e') });
 447 Irssi::command_bind('sr' => sub { cmd_modelist_show('R') });
 448 Irssi::command_bind('uninvite' => sub { cmd_modelist_del('I', $_[0]) });
 449 Irssi::command_bind('unexcept' => sub { cmd_modelist_del('e', $_[0]) });
 450 Irssi::command_bind('unreop' => sub { cmd_modelist_del('R', $_[0]) });
 451 
 452 sb_update();
 453 
 454 Irssi::statusbar_item_register('modelist', undef, 'sb_modelist');
 455 Irssi::statusbars_recreate_items();
 456 
 457 foreach my $server ( Irssi::servers )
 458 {
 459    foreach my $chanrec ( $server->channels )
 460    {
 461       create_channel($chanrec, 1);  
 462    }
 463 }
 464 
 465 
 466 
 467