diff options
| author | ailin-nemui | 2016-08-16 21:41:16 +0200 | 
|---|---|---|
| committer | GitHub | 2016-08-16 21:41:16 +0200 | 
| commit | 6bd98bdce33f933846c1fb7e373a92a0f930dc45 (patch) | |
| tree | e44a80c340c855b1d85254729c5ae880ad8e6e08 | |
| parent | eab456c01b8c4393f82dfe95897a90def31aa346 (diff) | |
| parent | 95fb0b8e4e44f3f7aee5e48496a6a21e887619e8 (diff) | |
| download | scripts.irssi.org-6bd98bdce33f933846c1fb7e373a92a0f930dc45.tar.bz2 | |
Merge pull request #293 from ailin-nemui/gh-pages
import anames script
| -rw-r--r-- | scripts/anames.pl | 438 | 
1 files changed, 438 insertions, 0 deletions
| diff --git a/scripts/anames.pl b/scripts/anames.pl new file mode 100644 index 0000000..385ffe6 --- /dev/null +++ b/scripts/anames.pl @@ -0,0 +1,438 @@ +# anames.pl + +# Commands +# ======== +# /anames +# * a clone of /names, with away nicks greyed out. + +# Options +# ======= +# /format endofanames +# * equivalent of /format endofnames, the final summary line of /anames command +# +# /format names_awaynick +# * colour for the nicks that are away, example: %w$0 +# +# /set anames_force_sync <ON|OFF> +# * whether to use -sync by default (request fresh /who list) +#   if you do not turn this on you will have to manually refresh the +#   away list using /who #channel, /anames -sync or you can turn it +#   off if you are running the autowho script to automatically update +#   the who list periodically + +# Thanks to Dirm and Chris62vw for the Perl help and coekie for writing the +# evil code to sort the nicklist by the alphabet and rank in nicklist.pl +# +# 1.6   - optional support for unicode nicknames, realnames script, +#         formattable summary line (default away colour changed!) (Nei) +# +# 1.5   - Fixed halfop display bug (patch by epinephrine), 20100712 +# +# 1.4   - Merged changes from VMiklos and readded /who redirection to prevent +#         spamming the status window. - ms, 20090122 +# +# 1.3   - by VMiklos +#         Doing /dowho is very annoying and /alias foo /dowho;/anames won't +#         work either since anames will work from the old infos. So I've +#         modified /anames to just do a /dowho and the nicklist will be printed +#         when we'll get the answer from the server. +# +# 1.2   - It seems that redirected events will not pass through the internal +#         mechanisms that update user information (like away states). So, it +#         /dowho and the periodic execution of the command has been disabled. +#         /anames will still work, but new away information will need to be +#         obtained by executing a /who on a channel. +#         If you can make redirection (execute a /who without the information +#         spilling to the status window) work, let me know so I can fix the +#         script. +# +# 1.0.1 - Fixed row-determining and max-nick-length code, changed command_add +#         calls to refs instead of names. +# +# 1.0   - Added timer for periodic /who of all channels +# +# 0.9   - Initial test release + +use strict; +use warnings; +use Irssi 20140918; +use List::Util qw(min max); + +use vars qw($VERSION %IRSSI); + +$VERSION = '1.6'; # 6eb152135b1bc6d +%IRSSI = ( +  authors     => 'Matt "f0rked" Sparks, Miklos Vajna', +  contact     => 'ms+irssi@quadpoint.org', +  name        => 'anames', +  description => 'a /names display with away nicks coloured', +  license     => 'GPLv2', +  url         => 'http://quadpoint.org', +); + + +my $tmp_server; +my $tmp_chan; +my $tmp_count; + + +Irssi::theme_register([ +  'endofanames' => '{channel $0}: Total of {hilight $1} nicks {comment {hilight $2} ops, {hilight $3} halfops, {hilight $4} voices, {hilight $5} normal, {hilight $6} away}', +  'names_awaynick' => '{channick $0}', +]); + + +sub cmd_help { +    my ($args) = @_; +    if ($args =~ /^anames *$/i) { +        print CLIENTCRAP <<HELP +%9Syntax:%9 + +ANAMES [-sync | -cached] [-count] [-<server tag>] [<channel>] + +%9Parameters:%9 + +    -sync:       Synchronise the away state of the channel. +    -cached:     Do not synchronise the away state, use cached info. +    -count:      Displays the amount of users away in the channel. + +    If no arguments are given, the users in the active channel will be +    displayed. + +%9Description:%9 + +    Display the users who are in channel and grey out those who are away. + +%9See also:%9 NAMES, WHO, WHOIS +HELP + +    } +} + + +{ +  local $@; +  eval { require Text::CharWidth; }; +  unless ($@) { +    *screen_length = sub { Text::CharWidth::mbswidth($_[0]) }; +  } else { +    *screen_length = sub { length($_[0]); } +  } +} + + +sub object_printformat_module { +  my ($object, $level, $module, $format, @args) = @_; +  { +    local *CORE::GLOBAL::caller = sub { $module }; +    $object->printformat($level, $format, @args); +  } +} + + +sub core_printformat_module { +  my ($level, $module, $format, @args) = @_; +  { +    local *CORE::GLOBAL::caller = sub { $module }; +    Irssi::printformat($level, $format, @args); +  } +} + + +sub cmd_anames +{ +  my($args, $server, $item) = @_; +  my $channel = $item; +  $tmp_server = $server->{"tag"}; +  $tmp_chan = $channel->{"name"}; +  $tmp_count = undef; + +  my ($force_sync, $force_cache); +  my @args = split ' ', $args; +  while (@args && $args[0] =~ /^-/) { +    if (lc $args[0] eq '-sync') { +      $force_sync = 1; shift @args; +    } +    elsif (lc $args[0] eq '-cached') { +      $force_cache = 1; shift @args; +    } +    elsif (lc $args[0] eq '-count') { +      $tmp_count = 1; shift @args; +    } +    else { +      last; +    } +  } +  if (@args) { +    if ($args[0] =~ /-(.*)/) { +      $tmp_server = $1; +      $server = Irssi::server_find_tag($tmp_server); +      shift @args; +    } +    unless (@args) { +      core_printformat_module(MSGLEVEL_CLIENTERROR, 'fe-common/core', 'not_enough_params'); +      return; +    } +    $tmp_chan = $args[0]; +  } + +  unless ($server) { +    core_printformat_module(MSGLEVEL_CLIENTERROR, 'fe-common/core', 'not_connected'); +    return; +  } + +  # set up redirection +  my $sync = Irssi::settings_get_bool('anames_force_sync'); +  my $irc = $server->isa('Irssi::Irc::Server'); +  if ($irc && ($force_sync || ($sync && !$force_cache))) { +    $server->redirect_event("who", 1, $tmp_chan, 0, "", +                              { +                                "event 352" => "silent event who", +                                "event 315" => "redir who_reply_end", +                              }); + +    $server->command("who $tmp_chan"); +  } elsif ($force_sync) { +    print CLIENTERROR "anames -sync is not supported for the chat protocol of the target server"; +  } else {  +    print_anames(); +  } +} + + +sub print_anames +{ +  my $server = Irssi::server_find_tag($tmp_server); +  my $chan = $tmp_chan; +  my $channel = $server ? $server->channel_find($chan) : undef; +  my $nick; + +  if (!$channel) { +    # no nicklist +    core_printformat_module(MSGLEVEL_CLIENTERROR, 'fe-common/core', 'not_joined'); +  } else { +    # Loop through each nick and display +    my @nicks; +    my($ops, $halfops, $voices, $normal, $away) = (0, 0, 0, 0, 0); + +    my $prefer_real; +    if (exists $Irssi::Script::{'realnames::'}) { +        my $code = "Irssi::Script::realnames"->can('use_realnames'); +        $prefer_real = $code && $code->($channel); +    } +    my $_real = sub { +        my $nick = shift; +        $prefer_real && length $nick->{'realname'} ? $nick->{'realname'} : $nick->{'nick'} +    }; +    # sorting from nicklist.pl +    foreach my $nick (sort {($a->{'op'}?'1':$a->{'halfop'}?'2':$a->{'voice'}?'3':$a->{'other'}>32?'0':'4').lc($_real->($a)) +        cmp ($b->{'op'}?'1':$b->{'halfop'}?'2':$b->{'voice'}?'3':$b->{'other'}>32?'0':'4').lc($_real->($b))} $channel->nicks()) { +      my $realnick = $_real->($nick); +      my $gone = $nick->{'gone'}; + +      my $prefix; +      if ($nick->{'other'}) { +        $prefix = chr $nick->{'other'}; +      } elsif ($nick->{'op'}) { +        $prefix = "@"; +      } elsif ($nick->{'halfop'}) { +        $prefix = "%"; +      } elsif ($nick->{'voice'}) { +        $prefix = "+"; +      } else { +        $prefix = " "; +      } + +      my $format; +      if ($nick->{'op'}) { +        $ops++; +        $format = 'names_nick_op'; +      } elsif ($nick->{'halfop'}) { +        $halfops++; +        $format = 'names_nick_halfop'; +      } elsif ($nick->{'voice'}) { +        $voices++; +        $format = 'names_nick_voice'; +      } else { +        $normal++; +        $format = 'names_nick'; +      } + +      if ($gone) { +        $realnick = $channel->window->format_get_text(__PACKAGE__, $server, $chan, 'names_awaynick', $realnick); +        $away++; +      } +      my $text = $channel->window->format_get_text('fe-common/core', $server, $chan, $format, $prefix, $realnick); +      my $bleak = Irssi::strip_codes($text); + +      push @nicks, [ $prefix, $realnick, $format, screen_length($bleak) ]; +    } + +    my $total = @nicks; +    unless ($tmp_count) { +      object_printformat_module($channel, MSGLEVEL_CLIENTCRAP, 'fe-common/core', 'names', $chan); +      columnize_nicks($channel, @nicks); +    } +    $channel->printformat(MSGLEVEL_CLIENTNOTICE, 'endofanames', $chan, $total, $ops, +                          $halfops, $voices, $normal, $away); +  } +} + +# src/core/misc.c +sub get_max_column_count { +  my $max_width = pop(@_) - 1; +  my @item_info = @_; + +  my $items_count = @item_info; +  if ($items_count == 0) { +    return; +  } + +  my $min_len = max 1, min map { $_->[-1] } @item_info; +  my $max_columns = max 1, int($max_width/$min_len); + +  my (@columns, @columns_width, @columns_rows); + +  for my $n (1 .. $max_columns - 1) { +    $columns_rows[$n] = $items_count <= $n+1 ? 1 : +                        ($items_count+$n)/($n+1); +  } + +  # for each possible column count, save the column widths and +  # find the biggest column count that fits to screen. +  my $item_pos = 0; +  my $max_len = max 1, map { $_->[-1] } @item_info; +  for my $tmp (@item_info) { +    my $len = $tmp->[-1]; + +    for my $n (1 .. $max_columns - 1) { +      no warnings 'uninitialized'; +      if ($columns_width[$n] > $max_width) { +	next; # too wide +      } + +      my $col = $item_pos/$columns_rows[$n]; +      if ($columns[$n][$col] < $len) { +	$columns_width[$n] += $len - $columns[$n][$col]; +	$columns[$n][$col] = $len; +      } +    } + +    $item_pos++; +  } + +  for my $n (reverse 1 .. $max_columns - 1) { +    no warnings 'uninitialized'; +    if ($columns_width[$n] <= $max_width && +	$columns[$n][$n] > 0) { +      return $n + 1; +    } +  } + +  return 1; +} + + +{ my %strip_table = ( +    # fe-common::core::formats.c:format_expand_styles +    #      delete                format_backs  format_fores bold_fores   other stuff +    (map { $_ => '' } (split //, '04261537' .  'kbgcrmyw' . 'KBGCRMYW' . 'U9_8I:|FnN>#[' . 'pP')), +    #      escape +    (map { $_ => $_ } (split //, '{}%')), +   ); +  sub ir_strip_codes { # strip %codes +    my $o = shift; +    $o =~ s/(%(%|Z.{6}|z.{6}|X..|x..|.))/exists $strip_table{$2} ? $strip_table{$2} : +	$2 =~ m{x(?:0[a-f]|[1-6][0-9a-z]|7[a-x])|z[0-9a-f]{6}}i ? '' : $1/gex; +    $o +  } +} + + +# create a /names style column, increasing alphabetically going down the +# columns. +sub columnize_nicks +{ +  my($channel, @nicks) = @_; +  my $total = @nicks; + +  # determine max columns +  my $cols = Irssi::settings_get_int("names_max_columns"); +  my $width = $channel->window->{width}; +  { +    my $ts_format = Irssi::settings_get_str('timestamp_format'); +    my $render_str = Irssi::current_theme->format_expand( +      Irssi::current_theme->get_format('fe-common/core', 'timestamp')); +    (my $ts_escaped = $ts_format) =~ s/([%\$])/$1$1/g; +    $render_str =~ s/(?|\$(.)(?!\w)|\$\{(\w+)\})/$1 eq 'Z' ? $ts_escaped : $1/ge; +    $render_str = ir_strip_codes($render_str); +    $width -= screen_length($render_str); +  } +  $width = max 10, $width; +  my $max_cols = get_max_column_count(@nicks, $width - 1); +  return unless $max_cols; +  if ($cols < 1) { +    $cols = $max_cols; +  } +  $cols = min $max_cols, $cols; + +  # determine number of rows +  my $rows = int($total / $cols) + !!($total % $cols); + +  # array of rows +  my @r; +  for (my $i = 0; $i < $cols; $i++) { +    # peek at next $rows items, determine max length +    my $max_length = max map { $_->[-1] } grep { defined } @nicks[0 .. $rows - 1]; + +    # fill rows +    for (my $j = 0; $j < $rows; $j++) { +      my $n = shift @nicks;  # single nick +      if ($n->[-1]) { +        $r[$j] .= $channel->window->format_get_text('fe-common/core', $channel->{server}, $channel->{visible_name}, +          $n->[2], $n->[0], $n->[1] . fill_spaces($n->[-1], $max_length) ); +      } +    } +  } + +  for (my $m = 0; $m < $rows; $m++) { +    chomp $r[$m]; +    $r[$m] =~ s/%/%%/g; +    $channel->print($r[$m], MSGLEVEL_CLIENTCRAP); +  } +} + + +sub fill_spaces +{ +  my($length, $max_length) = @_; +  return " " x max (0, $max_length - $length); +} + + +sub round +{ +  my($number) = @_; +  return int($number + .5); +} + + +sub who_reply_end +{ +  print_anames(); +#  Irssi::signal_emit('chanquery who end', @_); +  $tmp_chan = ""; +} + + +Irssi::Irc::Server::redirect_register("who", 0, 0, +                                      {"event 352" => 1}, +                                      {"event 315" => 1}, +                                      undef); +Irssi::signal_register({'chanquery who end' => [qw[iobject string]]}); +Irssi::signal_add("redir who_reply", 'who_reply'); +Irssi::signal_add("redir who_reply_end", 'who_reply_end'); +Irssi::settings_add_bool("anames", "anames_force_sync", 0); +Irssi::command_bind("anames", 'cmd_anames'); +Irssi::command_set_options("anames", "sync cached count"); +Irssi::command_bind_last('help' => 'cmd_help'); | 
