diff options
Diffstat (limited to '')
| -rw-r--r-- | .irssi/scripts/autoaway.pl | 133 | ||||
| l--------- | .irssi/scripts/autorun/autoaway.pl | 1 | ||||
| l--------- | .irssi/scripts/autorun/tmux_away.pl | 1 | ||||
| l--------- | .irssi/scripts/autorun/trackbar.pl | 1 | ||||
| l--------- | .irssi/scripts/autorun/trackbar22.pl | 1 | ||||
| -rw-r--r-- | .irssi/scripts/nickcolor.pl | 122 | ||||
| -rw-r--r-- | .irssi/scripts/old/nickcolor.pl.old | 145 | ||||
| -rw-r--r-- | .irssi/scripts/old/trackbar22.pl.old (renamed from .irssi/scripts/trackbar22.pl) | 0 | ||||
| -rw-r--r-- | .irssi/scripts/scriptassist.pl | 1229 | ||||
| -rw-r--r-- | .irssi/scripts/tmux_away.pl | 202 | ||||
| -rw-r--r-- | .irssi/scripts/trackbar.pl | 579 | 
11 files changed, 2406 insertions, 8 deletions
diff --git a/.irssi/scripts/autoaway.pl b/.irssi/scripts/autoaway.pl new file mode 100644 index 0000000..ef3c485 --- /dev/null +++ b/.irssi/scripts/autoaway.pl @@ -0,0 +1,133 @@ +# /AUTOAWAY <n> - Mark user away after <n> seconds of inactivity +# /AWAY - play nice with autoaway +# New, brighter, whiter version of my autoaway script. Actually works :) +# (c) 2000 Larry Daffner (vizzie@airmail.net) +#     You may freely use, modify and distribute this script, as long as +#      1) you leave this notice intact +#      2) you don't pretend my code is yours +#      3) you don't pretend your code is mine +# +# share and enjoy! + +# A simple script. /autoaway <n> will mark you as away automatically if +# you have not typed any commands in <n> seconds. (<n>=0 disables the feature) +# It will also automatically unmark you away the next time you type a command. +# Note that using the /away command will disable the autoaway mechanism, as +# well as the autoreturn. (when you unmark yourself, the autoaway wil +# restart again) + +# Thanks to Adam Monsen for multiserver and config file fix + +use strict; +use Irssi; +use Irssi::Irc; + +use vars qw($VERSION %IRSSI); +$VERSION = "0.4"; +%IRSSI = ( +    authors => 'Larry "Vizzie" Daffner', +    contact => 'vizzie@airmail.net', +    name => 'Automagic away setting', +    description => 'Automatically goes  away after defined inactivity', +    license => 'BSD', +    url => 'http://www.flamingpackets.net/~vizzie/irssi/', +    changed => 'Tue Apr 26 19:30:00 CDT 2016', +    changes => 'Applied multiserver/store config patch from Adam Monsen' +); + +my ($autoaway_sec, $autoaway_to_tag, $autoaway_state); +$autoaway_state = 0; + +# +# /AUTOAWAY - set the autoaway timeout +# +sub cmd_autoaway { +  my ($data, $server, $channel) = @_; +   +  if (!($data =~ /^[0-9]+$/)) { +    Irssi::print("autoaway: usage: /autoaway <seconds>"); +    return 1; +  } +   +  $autoaway_sec = $data; +   +  if ($autoaway_sec) { +    Irssi::settings_set_int("autoaway_timeout", $autoaway_sec); +    Irssi::print("autoaway timeout set to $autoaway_sec seconds"); +  } else { +    Irssi::print("autoway disabled"); +  } +   +  if (defined($autoaway_to_tag)) { +    Irssi::timeout_remove($autoaway_to_tag); +    $autoaway_to_tag = undef; +  } + +  if ($autoaway_sec) { +    $autoaway_to_tag = +      Irssi::timeout_add($autoaway_sec*1000, "auto_timeout", ""); +  } +} + +# +# away = Set us away or back, within the autoaway system +sub cmd_away { +  my ($data, $server, $channel) = @_; +   +  if ($data eq "") { +    $autoaway_state = 0; +  } else { +    if ($autoaway_state eq 0) { +      Irssi::timeout_remove($autoaway_to_tag); +      $autoaway_to_tag = undef; +      $autoaway_state = 2; +    } +  } +} + +sub auto_timeout { +  my ($data, $server) = @_; + +  # we're in the process.. don't touch anything. +  $autoaway_state = 3; +  foreach my $server (Irssi::servers()) { +      $server->command("/AWAY autoaway after $autoaway_sec seconds"); +  } + +  Irssi::timeout_remove($autoaway_to_tag); +  $autoaway_state = 1; +} + +sub reset_timer { +   if ($autoaway_state eq 1) { +     $autoaway_state = 3; +     foreach my $server (Irssi::servers()) { +         $server->command("/AWAY"); +     } +      +     $autoaway_state = 0; +   }  +  if ($autoaway_state eq 0) { +    if (defined($autoaway_to_tag)) { +      Irssi::timeout_remove($autoaway_to_tag); +      $autoaway_to_tag = undef(); +    } +    if ($autoaway_sec) { +      $autoaway_to_tag = Irssi::timeout_add($autoaway_sec*1000 +					    , "auto_timeout", ""); +    } +  } +} + +Irssi::settings_add_int("misc", "autoaway_timeout", 0); + +my $autoaway_default = Irssi::settings_get_int("autoaway_timeout"); +if ($autoaway_default) { +  $autoaway_to_tag = +    Irssi::timeout_add($autoaway_default*1000, "auto_timeout", ""); + +} + +Irssi::command_bind('autoaway', 'cmd_autoaway'); +Irssi::command_bind('away', 'cmd_away'); +Irssi::signal_add('send command', 'reset_timer'); diff --git a/.irssi/scripts/autorun/autoaway.pl b/.irssi/scripts/autorun/autoaway.pl new file mode 120000 index 0000000..a6c9910 --- /dev/null +++ b/.irssi/scripts/autorun/autoaway.pl @@ -0,0 +1 @@ +../autoaway.pl
\ No newline at end of file diff --git a/.irssi/scripts/autorun/tmux_away.pl b/.irssi/scripts/autorun/tmux_away.pl new file mode 120000 index 0000000..70df40e --- /dev/null +++ b/.irssi/scripts/autorun/tmux_away.pl @@ -0,0 +1 @@ +../tmux_away.pl
\ No newline at end of file diff --git a/.irssi/scripts/autorun/trackbar.pl b/.irssi/scripts/autorun/trackbar.pl new file mode 120000 index 0000000..8c5a483 --- /dev/null +++ b/.irssi/scripts/autorun/trackbar.pl @@ -0,0 +1 @@ +../trackbar.pl
\ No newline at end of file diff --git a/.irssi/scripts/autorun/trackbar22.pl b/.irssi/scripts/autorun/trackbar22.pl deleted file mode 120000 index 09c98ff..0000000 --- a/.irssi/scripts/autorun/trackbar22.pl +++ /dev/null @@ -1 +0,0 @@ -../trackbar22.pl
\ No newline at end of file diff --git a/.irssi/scripts/nickcolor.pl b/.irssi/scripts/nickcolor.pl index ff9915d..95b7b63 100644 --- a/.irssi/scripts/nickcolor.pl +++ b/.irssi/scripts/nickcolor.pl @@ -1,24 +1,36 @@  use strict;  use Irssi 20020101.0250 (); -use vars qw($VERSION %IRSSI);  -$VERSION = "2"; +use vars qw($VERSION %IRSSI); +$VERSION = "2.1";  %IRSSI = ( -    authors     => "Timo Sirainen, Ian Peters, David Leadbeater", -    contact	=> "tss\@iki.fi",  +    authors     => "Timo Sirainen, Ian Peters, David Leadbeater, Bruno Cattáneo", +    contact	=> "tss\@iki.fi",      name        => "Nick Color",      description => "assign a different color for each nick",      license	=> "Public Domain",      url		=> "http://irssi.org/", -    changed	=> "Sun 15 Jun 19:10:44 BST 2014", +    changed	=> "Mon 08 Jan 21:28:53 BST 2018",  );  # Settings:  #   nickcolor_colors: List of color codes to use.  #   e.g. /set nickcolor_colors 2 3 4 5 6 7 9 10 11 12 13  #   (avoid 8, as used for hilights in the default theme). +# +#   nickcolor_enable_prefix: Enables prefix for same nick. +# +#   nickcolor_enable_truncate: Enables nick truncation. +# +#   nickcolor_prefix_text: Prefix text for succesive messages. +#   e.g. /set nickcolor_prefix_text - +# +#   nickcolor_truncate_value: Truncate nick value. +#   e.g. /set nickcolor_truncate_value -7 +#   This will truncate nicknames at 7 characters and make them right aligned  my %saved_colors;  my %session_colors = {}; +my %saved_nicks; # To store each channel's last nickname  sub load_colors {    open my $color_fh, "<", "$ENV{HOME}/.irssi/saved_colors"; @@ -75,9 +87,21 @@ sub simple_hash {    return $counter;  } +# process public (others) messages  sub sig_public {    my ($server, $msg, $nick, $address, $target) = @_; +  my $enable_prefix = Irssi::settings_get_bool('nickcolor_enable_prefix'); +  my $enable_truncate = Irssi::settings_get_bool('nickcolor_enable_truncate'); +  my $prefix_text = Irssi::settings_get_str('nickcolor_prefix_text'); +  my $truncate_value = Irssi::settings_get_int('nickcolor_truncate_value'); + +  # Reference for server/channel +  my $tagtarget = "$server->{tag}/$target"; + +  # Set default nick truncate value to 0 if option is disabled +  $truncate_value = 0 if (!$enable_truncate); +    # Has the user assigned this nick a color?    my $color = $saved_colors{$nick}; @@ -93,7 +117,84 @@ sub sig_public {    }    $color = sprintf "\003%02d", $color; -  $server->command('/^format pubmsg {pubmsgnick $2 {pubnick ' . $color . '$0}}$1'); + +  # Optional: We check if it's the same nickname for current target +  if ($saved_nicks{$tagtarget} eq $nick && $enable_prefix) +  { +    # Grouped message +    Irssi::command('/^format pubmsg ' . $prefix_text . '$1'); +  } +  else +  { +    # Normal message +    Irssi::command('/^format pubmsg {pubmsgnick $2 {pubnick ' . $color . '$[' . $truncate_value . ']0}}$1'); + +    # Save nickname for next message +    $saved_nicks{$tagtarget} = $nick; +  } + +} + +# process public (me) messages +sub sig_me { +  my ($server, $msg, $target) = @_; +  my $nick = $server->{nick}; + +  my $enable_prefix = Irssi::settings_get_bool('nickcolor_enable_prefix'); +  my $enable_truncate = Irssi::settings_get_bool('nickcolor_enable_truncate'); +  my $prefix_text = Irssi::settings_get_str('nickcolor_prefix_text'); +  my $truncate_value = Irssi::settings_get_int('nickcolor_truncate_value'); + +  # Reference for server/channel +  my $tagtarget = "$server->{tag}/$target"; + +  # Set default nick truncate value to 0 if option is disabled +  $truncate_value = 0 if (!$enable_truncate); + +  # Optional: We check if it's the same nickname for current target +  if ($saved_nicks{$tagtarget} eq $nick && $enable_prefix) +  { +    # Grouped message +    Irssi::command('/^format own_msg ' . $prefix_text . '$1'); +  } +  else +  { +    # Normal message +    Irssi::command('/^format own_msg {ownmsgnick $2 {ownnick $[' . $truncate_value . ']0}}$1'); + +    # Save nickname for next message +    $saved_nicks{$tagtarget} = $nick; +  } + +} + +# process public (others) actions +sub sig_action_public { +  my ($server, $msg, $nick, $address, $target) = @_; + +  my $enable_prefix = Irssi::settings_get_bool('nickcolor_enable_prefix'); + +  # Reference for server/channel +  my $tagtarget = "$server->{tag}/$target"; + +  # Empty current target nick if prefix option is enabled +  $saved_nicks{$tagtarget} = '' if ($enable_prefix); + +} + +# process public (me) actions +sub sig_action_me { +  my ($server, $msg, $target) = @_; +  my $nick = $server->{nick}; + +  my $enable_prefix = Irssi::settings_get_bool('nickcolor_enable_prefix'); + +  # Reference for server/channel +  my $tagtarget = "$server->{tag}/$target"; + +  # Empty current target nick if prefix option is enabled +  $saved_nicks{$tagtarget} = '' if ($enable_prefix); +  }  sub cmd_color { @@ -125,7 +226,7 @@ sub cmd_color {    } elsif ($op eq "list") {      Irssi::print ("\nSaved Colors:");      foreach my $nick (keys %saved_colors) { -      Irssi::print (chr (3) . "$saved_colors{$nick}$nick" . +      Irssi::print (chr (3) . sprintf("%02d", $saved_colors{$nick}) . "$nick" .  		    chr (3) . "1 ($saved_colors{$nick})");      }    } elsif ($op eq "preview") { @@ -139,7 +240,14 @@ sub cmd_color {  load_colors;  Irssi::settings_add_str('misc', 'nickcolor_colors', '2 3 4 5 6 7 9 10 11 12 13'); +Irssi::settings_add_bool('misc', 'nickcolor_enable_prefix', 0); +Irssi::settings_add_bool('misc', 'nickcolor_enable_truncate', 0); +Irssi::settings_add_str('misc', 'nickcolor_prefix_text' => '- '); +Irssi::settings_add_int('misc', 'nickcolor_truncate_value' => 0);  Irssi::command_bind('color', 'cmd_color');  Irssi::signal_add('message public', 'sig_public'); +Irssi::signal_add('message own_public', 'sig_me'); +Irssi::signal_add('message irc action', 'sig_action_public'); +Irssi::signal_add('message irc own_action', 'sig_action_me');  Irssi::signal_add('event nick', 'sig_nick'); diff --git a/.irssi/scripts/old/nickcolor.pl.old b/.irssi/scripts/old/nickcolor.pl.old new file mode 100644 index 0000000..ff9915d --- /dev/null +++ b/.irssi/scripts/old/nickcolor.pl.old @@ -0,0 +1,145 @@ +use strict; +use Irssi 20020101.0250 (); +use vars qw($VERSION %IRSSI);  +$VERSION = "2"; +%IRSSI = ( +    authors     => "Timo Sirainen, Ian Peters, David Leadbeater", +    contact	=> "tss\@iki.fi",  +    name        => "Nick Color", +    description => "assign a different color for each nick", +    license	=> "Public Domain", +    url		=> "http://irssi.org/", +    changed	=> "Sun 15 Jun 19:10:44 BST 2014", +); + +# Settings: +#   nickcolor_colors: List of color codes to use. +#   e.g. /set nickcolor_colors 2 3 4 5 6 7 9 10 11 12 13 +#   (avoid 8, as used for hilights in the default theme). + +my %saved_colors; +my %session_colors = {}; + +sub load_colors { +  open my $color_fh, "<", "$ENV{HOME}/.irssi/saved_colors"; +  while (<$color_fh>) { +    chomp; +    my($nick, $color) = split ":"; +    $saved_colors{$nick} = $color; +  } +} + +sub save_colors { +  open COLORS, ">", "$ENV{HOME}/.irssi/saved_colors"; + +  foreach my $nick (keys %saved_colors) { +    print COLORS "$nick:$saved_colors{$nick}\n"; +  } + +  close COLORS; +} + +# If someone we've colored (either through the saved colors, or the hash +# function) changes their nick, we'd like to keep the same color associated +# with them (but only in the session_colors, ie a temporary mapping). + +sub sig_nick { +  my ($server, $newnick, $nick, $address) = @_; +  my $color; + +  $newnick = substr ($newnick, 1) if ($newnick =~ /^:/); + +  if ($color = $saved_colors{$nick}) { +    $session_colors{$newnick} = $color; +  } elsif ($color = $session_colors{$nick}) { +    $session_colors{$newnick} = $color; +  } +} + +# This gave reasonable distribution values when run across +# /usr/share/dict/words + +sub simple_hash { +  my ($string) = @_; +  chomp $string; +  my @chars = split //, $string; +  my $counter; + +  foreach my $char (@chars) { +    $counter += ord $char; +  } + +  my @colors = split / /, Irssi::settings_get_str('nickcolor_colors'); +  $counter = $colors[$counter % @colors]; + +  return $counter; +} + +sub sig_public { +  my ($server, $msg, $nick, $address, $target) = @_; + +  # Has the user assigned this nick a color? +  my $color = $saved_colors{$nick}; + +  # Have -we- already assigned this nick a color? +  if (!$color) { +    $color = $session_colors{$nick}; +  } + +  # Let's assign this nick a color +  if (!$color) { +    $color = simple_hash $nick; +    $session_colors{$nick} = $color; +  } + +  $color = sprintf "\003%02d", $color; +  $server->command('/^format pubmsg {pubmsgnick $2 {pubnick ' . $color . '$0}}$1'); +} + +sub cmd_color { +  my ($data, $server, $witem) = @_; +  my ($op, $nick, $color) = split " ", $data; + +  $op = lc $op; + +  if (!$op) { +    Irssi::print ("No operation given (save/set/clear/list/preview)"); +  } elsif ($op eq "save") { +    save_colors; +  } elsif ($op eq "set") { +    if (!$nick) { +      Irssi::print ("Nick not given"); +    } elsif (!$color) { +      Irssi::print ("Color not given"); +    } elsif ($color < 2 || $color > 14) { +      Irssi::print ("Color must be between 2 and 14 inclusive"); +    } else { +      $saved_colors{$nick} = $color; +    } +  } elsif ($op eq "clear") { +    if (!$nick) { +      Irssi::print ("Nick not given"); +    } else { +      delete ($saved_colors{$nick}); +    } +  } elsif ($op eq "list") { +    Irssi::print ("\nSaved Colors:"); +    foreach my $nick (keys %saved_colors) { +      Irssi::print (chr (3) . "$saved_colors{$nick}$nick" . +		    chr (3) . "1 ($saved_colors{$nick})"); +    } +  } elsif ($op eq "preview") { +    Irssi::print ("\nAvailable colors:"); +    foreach my $i (2..14) { +      Irssi::print (chr (3) . "$i" . "Color #$i"); +    } +  } +} + +load_colors; + +Irssi::settings_add_str('misc', 'nickcolor_colors', '2 3 4 5 6 7 9 10 11 12 13'); +Irssi::command_bind('color', 'cmd_color'); + +Irssi::signal_add('message public', 'sig_public'); +Irssi::signal_add('event nick', 'sig_nick'); diff --git a/.irssi/scripts/trackbar22.pl b/.irssi/scripts/old/trackbar22.pl.old index 0d10882..0d10882 100644 --- a/.irssi/scripts/trackbar22.pl +++ b/.irssi/scripts/old/trackbar22.pl.old diff --git a/.irssi/scripts/scriptassist.pl b/.irssi/scripts/scriptassist.pl new file mode 100644 index 0000000..6870894 --- /dev/null +++ b/.irssi/scripts/scriptassist.pl @@ -0,0 +1,1229 @@ +# by Stefan "tommie" Tomanek +# +# scriptassist.pl + + +use strict; + +our $VERSION = '2003020806'; +our %IRSSI = ( +    authors     => 'Stefan \'tommie\' Tomanek', +    contact     => 'stefan@pico.ruhr.de', +    name        => 'scriptassist', +    description => 'keeps your scripts on the cutting edge', +    license     => 'GPLv2', +    url         => 'http://irssi.org/scripts/', +    modules     => 'Data::Dumper LWP::UserAgent (GnuPG)', +    commands	=> "scriptassist" +); + +our ($forked, %remote_db, $have_gpg, @complist); + +use Irssi 20020324; +use Data::Dumper; +use LWP::UserAgent; +use POSIX; + +# GnuPG is not always needed +$have_gpg = 0; +eval "use GnuPG qw(:algo :trust);"; +$have_gpg = 1 if not ($@); + +sub show_help { +    my $help = "scriptassist $VERSION +/scriptassist check +    Check all loaded scripts for new available versions +/scriptassist update <script|all> +    Update the selected or all script to the newest version +/scriptassist search <query> +    Search the script database +/scriptassist info <scripts> +    Display information about <scripts> +".#/scriptassist ratings <scripts> +#    Retrieve the average ratings of the the scripts +#/scriptassist top <num> +#    Retrieve the first <num> top rated scripts +"/scriptassist new <num> +    Display the newest <num> scripts +".#/scriptassist rate <script> <stars> +#    Rate the script with a number of stars ranging from 0-5 +"/scriptassist contact <script> +    Write an email to the author of the script +    (Requires OpenURL) +/scriptassist cpan <module> +    Visit CPAN to look for missing Perl modules +    (Requires OpenURL) +/scriptassist install <script> +    Retrieve and load the script +/scriptassist autorun <script> +    Toggles automatic loading of <script> +";   +    my $text=''; +    foreach (split(/\n/, $help)) { +        $_ =~ s/^\/(.*)$/%9\/$1%9/; +        $text .= $_."\n"; +    } +    print CLIENTCRAP &draw_box("ScriptAssist", $text, "scriptassist help", 1); +    #theme_box("ScriptAssist", $text, "scriptassist help", 1); +} + +sub theme_box { +    my ($title, $text, $footer, $colour) = @_; +    Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'box_header', $title); +    foreach (split(/\n/, $text)) { +	Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'box_inside', $_); +    } +    Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'box_footer', $footer); +} + +sub draw_box { +    my ($title, $text, $footer, $colour) = @_; +    my $box = ''; +    $box .= '%R,--[%n%9%U'.$title.'%U%9%R]%n'."\n"; +    foreach (split(/\n/, $text)) { +        $box .= '%R|%n '.$_."\n"; +    } +    $box .= '%R`--<%n'.$footer.'%R>->%n'; +    $box =~ s/%.//g unless $colour; +    return $box; +} + +sub call_openurl { +    my ($url) = @_; +    # check for a loaded openurl +    if (my $code = Irssi::Script::openurl::->can('launch_url')) { +	$code->($url); +    } else { +        print CLIENTCRAP "%R>>%n Please install openurl.pl"; +    } +} + +sub bg_do { +    my ($func) = @_; +    my ($rh, $wh); +    pipe($rh, $wh); +    if ($forked) { +	print CLIENTCRAP "%R>>%n Please wait until your earlier request has been finished."; +	return; +    } +    my $pid = fork(); +    $forked = 1; +    if ($pid > 0) { +	print CLIENTCRAP "%R>>%n Please wait..."; +        close $wh; +        Irssi::pidwait_add($pid); +        my $pipetag; +        my @args = ($rh, \$pipetag, $func); +        $pipetag = Irssi::input_add(fileno($rh), INPUT_READ, \&pipe_input, \@args); +    } else { +	eval { +	    my @items = split(/ /, $func); +	    my %result; +	    my $ts1 = $remote_db{timestamp}; +	    my $xml = get_scripts(); +	    my $ts2 = $remote_db{timestamp}; +	    if (not($ts1 eq $ts2) && Irssi::settings_get_bool('scriptassist_cache_sources')) { +		$result{db} = $remote_db{db}; +		$result{timestamp} = $remote_db{timestamp}; +	    } +	    if ($items[0] eq 'check') { +		$result{data}{check} = check_scripts($xml); +	    } elsif ($items[0] eq 'update') { +		shift(@items); +		$result{data}{update} = update_scripts(\@items, $xml); +	    } elsif ($items[0] eq 'search') { +		shift(@items); +		foreach (@items) { +		    $result{data}{search}{$_} = search_scripts($_, $xml); +		} +	    } elsif ($items[0] eq 'install') { +		shift(@items); +		$result{data}{install} = install_scripts(\@items, $xml); +	    } elsif ($items[0] eq 'debug') { +		shift(@items); +		$result{data}{debug} = debug_scripts(\@items); +	    } elsif ($items[0] eq 'ratings') { +		shift(@items); +		@items = @{ loaded_scripts() } if $items[0] eq "all"; +		my %ratings = %{ get_ratings(\@items, '') }; +		foreach (keys %ratings) { +		    $result{data}{rating}{$_}{rating} = $ratings{$_}->[0]; +		    $result{data}{rating}{$_}{votes} = $ratings{$_}->[1]; +		} +	    } elsif ($items[0] eq 'rate') { +		$result{data}{rate}{$items[1]} = rate_script($items[1], $items[2]); +	    } elsif ($items[0] eq 'info') { +		shift(@items); +		$result{data}{info} = script_info(\@items); +	    } elsif ($items[0] eq 'echo') { +		$result{data}{echo} = 1; +	    } elsif ($items[0] eq 'top') { +		my %ratings = %{ get_ratings([], $items[1]) }; +		foreach (keys %ratings) { +                    $result{data}{rating}{$_}{rating} = $ratings{$_}->[0]; +                    $result{data}{rating}{$_}{votes} = $ratings{$_}->[1]; +                } +	    } elsif ($items[0] eq 'new') { +		my $new = get_new($items[1]); +		$result{data}{new} = $new; +	    } elsif ($items[0] eq 'unknown') { +		my $cmd = $items[1]; +		$result{data}{unknown}{$cmd} = get_unknown($cmd, $xml); +	    } +	    my $dumper = Data::Dumper->new([\%result]); +	    $dumper->Purity(1)->Deepcopy(1)->Indent(0); +	    my $data = $dumper->Dump; +	    print($wh $data); +	}; +	if ($@) { +	    print($wh Data::Dumper->new([+{data=>+{error=>$@}}]) +		      ->Purity(1)->Deepcopy(1)->Indent(0)->Dump); +	} +	close($wh); +	POSIX::_exit(1); +    } +} + +sub get_unknown { +    my ($cmd, $db) = @_; +    foreach (keys %$db) { +	next unless defined $db->{$_}{commands}; +	foreach my $item (split / /, $db->{$_}{commands}) { +	    return { $_ => $db->{$_} } if ($item =~ /^$cmd$/i); +	} +    } +    return undef; +} + +sub get_names { +    my ($sname, $db) = shift; +    $sname =~ s/\s+$//; +    $sname =~ s/\.pl$//; +    my $plname = "$sname.pl"; +    $sname =~ s/^.*\///; +    my $xname = $sname; +    $xname =~ s/\W/_/g; +    my $pname = "${xname}::"; +    if ($xname ne $sname || $sname =~ /_/) { +	my $dir = Irssi::get_irssi_dir()."/scripts/"; +	if ($db && exists $db->{"$sname.pl"}) { +	    # $found = 1; +	} elsif (-e $dir.$plname || -e $dir."$sname.pl" || -e $dir."autorun/$sname.pl") { +	    # $found = 1; +	} else { +	    # not found +	    my $pat = $xname; $pat =~ y/_/?/; +	    my $re = "\Q$xname"; $re =~ s/\Q_/./g; +	    if ($db) { +		my ($cand) = grep /^$re\.pl$/, sort keys %$db; +		if ($cand) { +		    return get_names($cand, $db); +		} +	    } +	    my ($cand) = glob "'$dir$pat.pl' '${dir}autorun/$pat.pl'"; +	    if ($cand) { +		$cand =~ s/^.*\///; +		return get_names($cand, $db); +	    } +	} +    } +    ($sname, $plname, $pname, $xname) +} + +sub script_info { +    my ($scripts) = @_; +    my %result; +    my $xml = get_scripts(); +    foreach (@{$scripts}) { +	my ($sname, $plname, $pname) = get_names($_, $xml); +	next unless (defined $xml->{$plname} || ( exists $Irssi::Script::{$pname} && exists $Irssi::Script::{$pname}{IRSSI} )); +	$result{$sname}{version} = get_remote_version($sname, $xml); +	my @headers = ('authors', 'contact', 'description', 'license', 'source'); +	foreach my $entry (@headers) { +	    $result{$sname}{$entry} = $Irssi::Script::{$pname}{IRSSI}{$entry}; +	    if (defined $xml->{$plname}{$entry}) { +		$result{$sname}{$entry} = $xml->{$plname}{$entry}; +	    } +	} +	if ($xml->{$plname}{signature_available}) { +	    $result{$sname}{signature_available} = 1; +	} +	if (defined $xml->{$plname}{modules}) { +	    my $modules = $xml->{$plname}{modules}; +	    foreach my $mod (split(/ /, $modules)) { +		my $opt = ($mod =~ /\((.*)\)/)? 1 : 0; +		$mod = $1 if $1; +		$result{$sname}{modules}{$mod}{optional} = $opt; +		$result{$sname}{modules}{$mod}{installed} = module_exist($mod); +	    } +	} elsif (defined $Irssi::Script::{$pname}{IRSSI}{modules}) { +	    my $modules = $Irssi::Script::{$pname}{IRSSI}{modules}; +	    foreach my $mod (split(/ /, $modules)) { +		my $opt = ($mod =~ /\((.*)\)/)? 1 : 0; +		$mod = $1 if $1; +		$result{$sname}{modules}{$mod}{optional} = $opt; +		$result{$sname}{modules}{$mod}{installed} = module_exist($mod); +	    } +	} +	if (defined $xml->{$plname}{depends}) { +	    my $depends = $xml->{$plname}{depends}; +	    foreach my $dep (split(/ /, $depends)) { +		$result{$sname}{depends}{$dep}{installed} = 1; +	    } +	} +    } +    return \%result; +} + +sub rate_script { +    my ($script, $stars) = @_; +    my $ua = LWP::UserAgent->new(env_proxy=>1, keep_alive=>1, timeout=>30); +    $ua->agent('ScriptAssist/'.2003020803); +    my $request = HTTP::Request->new('GET', 'http://ratings.irssi.de/irssirate.pl?&stars='.$stars.'&mode=rate&script='.$script); +    my $response = $ua->request($request); +    unless ($response->is_success() && $response->content() =~ /You already rated this script/) { +	return 1; +    } else { +	return 0; +    } +} + +sub get_ratings { +    my ($scripts, $limit) = @_; +    my $ua = LWP::UserAgent->new(env_proxy=>1, keep_alive=>1, timeout=>30); +    $ua->agent('ScriptAssist/'.2003020803); +    my $script = join(',', @{$scripts}); +    my $request = HTTP::Request->new('GET', 'http://ratings.irssi.de/irssirate.pl?script='.$script.'&sort=rating&limit='.$limit); +    my $response = $ua->request($request); +    my %result; +    if ($response->is_success()) { +	foreach (split /\n/, $response->content()) { +	    if (/<tr><td><a href=".*?">(.*?)<\/a>/) { +		my $entry = $1; +		if (/"><\/td><td>([0-9.]+)<\/td><td>(.*?)<\/td><td>/) { +		    $result{$entry} = [$1, $2]; +		} +	    } +	} +    } +    return \%result; +} + +sub get_new { +    my ($num) = @_; +    my $result; +    my $xml = get_scripts(); +    foreach (sort {$xml->{$b}{last_modified} cmp $xml->{$a}{last_modified}} keys %$xml) { +	my %entry = %{ $xml->{$_} }; +	next if $entry{HIDDEN}; +	$result->{$_} = \%entry; +	$num--; +	last unless $num; +    } +    return $result; +} +sub module_exist { +    my ($module) = @_; +    $module =~ s/::/\//g; +    foreach (@INC) { +	return 1 if (-e $_."/".$module.".pm"); +    } +    return 0; +} + +sub debug_scripts { +    my ($scripts) = @_; +    my %result; +    my $xml = get_scripts(); +    foreach (@{$scripts}) { +	my ($sname, $plname) = get_names($_, $xml); +	if (defined $xml->{$plname}{modules}) { +	    my $modules = $xml->{$plname}{modules}; +	    foreach my $mod (split(/ /, $modules)) { +                my $opt = ($mod =~ /\((.*)\)/)? 1 : 0; +                $mod = $1 if $1; +                $result{$sname}{$mod}{optional} = $opt; +                $result{$sname}{$mod}{installed} = module_exist($mod); +	    } +	} +    } +    return(\%result); +} + +sub install_scripts { +    my ($scripts, $xml) = @_; +    my %success; +    my $dir = Irssi::get_irssi_dir()."/scripts/"; +    foreach (@{$scripts}) { +	my ($sname, $plname, $pname) = get_names($_, $xml); +	if (get_local_version($sname) && (-e $dir.$plname)) { +	    $success{$sname}{installed} = -2; +	} else { +	    $success{$sname} = download_script($sname, $xml); +	} +    } +    return \%success; +} + +sub update_scripts { +    my ($list, $database) = @_; +    $list = loaded_scripts() if ($list->[0] eq "all" || scalar(@$list) == 0); +    my %status; +    foreach (@{$list}) { +	my ($sname) = get_names($_, $database); +	my $local = get_local_version($sname); +	my $remote = get_remote_version($sname, $database); +	next if $local eq '' || $remote eq ''; +	if (compare_versions($local, $remote) eq "older") { +	    $status{$sname} = download_script($sname, $database); +	} else { +	    $status{$sname}{installed} = -2; +	} +	$status{$sname}{remote} = $remote; +	$status{$sname}{local} = $local; +    } +    return \%status; +} + +sub search_scripts { +    my ($query, $database) = @_; +    $query =~ s/\.pl\Z//; +    my %result; +    foreach (sort keys %{$database}) { +	my %entry = %{$database->{$_}}; +	next if $entry{HIDDEN}; +	my $string = $_." "; +	$string .= $entry{description} if defined $entry{description}; +	if ($string =~ /$query/i) { +	    my $name = $_; +	    $name =~ s/\.pl$//; +	    if (defined $entry{description}) { +		$result{$name}{desc} = $entry{description}; +	    } else { +		$result{$name}{desc} = ""; +	    } +	    if (defined $entry{authors}) { +		$result{$name}{authors} = $entry{authors}; +	    } else { +		$result{$name}{authors} = ""; +	    } +	    if (get_local_version($name)) { +		$result{$name}{installed} = 1; +	    } else { +		$result{$name}{installed} = 0; +	    } +	} +    } +    return \%result; +} + +sub pipe_input { +    my ($rh, $pipetag) = @{$_[0]}; +    my $text = do { local $/; <$rh>; }; +    close($rh); +    Irssi::input_remove($$pipetag); +    $forked = 0; +    unless ($text) { +	print CLIENTCRAP "%R<<%n Something weird happend (no text)"; +	return(); +    } +    local our $VAR1; +    my $incoming = eval($text); +    if ($incoming->{db} && $incoming->{timestamp}) { +    	$remote_db{db} = $incoming->{db}; +    	$remote_db{timestamp} = $incoming->{timestamp}; +    } +    unless (defined $incoming->{data}) { +	print CLIENTCRAP "%R<<%n Something weird happend (no data)"; +	return; +    } +    my %result = %{ $incoming->{data} }; +    @complist = (); +    if (defined $result{new}) { +	print_new($result{new}); +	push @complist, $_ foreach keys %{ $result{new} }; +    } +    if (defined $result{check}) { +	print_check(%{$result{check}}); +	push @complist, $_ foreach keys %{ $result{check} }; +    } +    if (defined $result{update}) { +	print_update(%{ $result{update} }); +	push @complist, $_ foreach keys %{ $result{update} }; +    } +    if (defined $result{search}) { +	foreach (keys %{$result{search}}) { +	    print_search($_, %{$result{search}{$_}}); +	    push @complist, keys(%{$result{search}{$_}}); +	} +    } +    if (defined $result{install}) { +	print_install(%{ $result{install} }); +	push @complist, $_ foreach keys %{ $result{install} }; +    } +    if (defined $result{debug}) { +	print_debug(%{ $result{debug} }); +    } +    if (defined $result{rating}) { +	print_ratings(%{ $result{rating} }); +	push @complist, $_ foreach keys %{ $result{rating} }; +    } +    if (defined $result{rate}) { +	print_rate(%{ $result{rate} }); +    } +    if (defined $result{info}) { +	print_info(%{ $result{info} }); +    } +    if (defined $result{echo}) { +	Irssi::print "ECHO"; +    } +    if ($result{unknown}) { +        print_unknown($result{unknown}); +    } +    if (defined $result{error}) { +	print CLIENTCRAP "%R<<%n There was an error in background processing:"; chomp($result{error}); +	print CLIENTERROR $result{error}; +    } + +} + +sub print_unknown { +    my ($data) = @_; +    foreach my $cmd (keys %$data) { +	print CLIENTCRAP "%R<<%n No script provides '/$cmd'" unless $data->{$cmd}; +	foreach (keys %{ $data->{$cmd} }) { +	    my $text .= "The command '/".$cmd."' is provided by the script '".$data->{$cmd}{$_}{name}."'.\n"; +	    $text .= "This script is currently not installed on your system.\n"; +	    $text .= "If you want to install the script, enter\n"; +	    my ($name) = get_names($_); +	    $text .= "  %U/script install ".$name."%U "; +	    my $output = draw_box("ScriptAssist", $text, "'".$_."' missing", 1); +	    print CLIENTCRAP $output; +	} +    } +} + +sub check_autorun { +    my ($script) = @_; +    my (undef, $plname) = get_names($script); +    my $dir = Irssi::get_irssi_dir()."/scripts/"; +    if (-e $dir."/autorun/".$plname) { +	if (readlink($dir."/autorun/".$plname) eq "../".$plname) { +	    return 1; +	} +    } +    return 0; +} + +sub array2table { +    my (@array) = @_; +    my @width; +    foreach my $line (@array) { +        for (0..scalar(@$line)-1) { +            my $l = $line->[$_]; +            $l =~ s/%[^%]//g; +            $l =~ s/%%/%/g; +            $width[$_] = length($l) if $width[$_]<length($l); +        } +    } +    my $text; +    foreach my $line (@array) { +        for (0..scalar(@$line)-1) { +            my $l = $line->[$_]; +            $text .= $line->[$_]; +            $l =~ s/%[^%]//g; +            $l =~ s/%%/%/g; +            $text .= " "x($width[$_]-length($l)+1) unless ($_ == scalar(@$line)-1); +        } +        $text .= "\n"; +    } +    return $text; +} + + +sub print_info { +    my (%data) = @_; +    my $line; +    foreach my $script (sort keys(%data)) { +	my ($local, $autorun); +	if (get_local_version($script)) { +	    $line .= "%go%n "; +	    $local = get_local_version($script); +	} else { +	    $line .= "%ro%n "; +	    $local = undef; +	} +	if (defined $local || check_autorun($script)) { +	    $autorun = "no"; +	    $autorun = "yes" if check_autorun($script); +	} else { +	    $autorun = undef; +	} +	$line .= "%9".$script."%9\n"; +	$line .= "  Version    : ".$data{$script}{version}."\n"; +	$line .= "  Source     : ".$data{$script}{source}."\n"; +	$line .= "  Installed  : ".$local."\n" if defined $local; +	$line .= "  Autorun    : ".$autorun."\n" if defined $autorun; +	$line .= "  Authors    : ".$data{$script}{authors}; +	$line .= " %Go-m signed%n" if $data{$script}{signature_available}; +	$line .= "\n"; +	$line .= "  Contact    : ".$data{$script}{contact}."\n"; +	$line .= "  Description: ".$data{$script}{description}."\n"; +	$line .= "\n" if $data{$script}{modules}; +	$line .= "  Needed Perl modules:\n" if $data{$script}{modules}; + +        foreach (sort keys %{$data{$script}{modules}}) { +            if ( $data{$script}{modules}{$_}{installed} == 1 ) { +                $line .= "  %g->%n ".$_." (found)"; +            } else { +                $line .= "  %r->%n ".$_." (not found)"; +            } +	    $line .= " <optional>" if $data{$script}{modules}{$_}{optional}; +            $line .= "\n"; +        } +	$line .= "  Needed Irssi Scripts:\n" if $data{$script}{depends}; +	foreach (sort keys %{$data{$script}{depends}}) { +	    if ( $data{$script}{depends}{$_}{installed} == 1 ) { +		$line .= "  %g->%n ".$_." (loaded)"; +	    } else { +		$line .= "  %r->%n ".$_." (not loaded)"; +	    } +	    $line .= "\n"; +	} +    } +    print CLIENTCRAP draw_box('ScriptAssist', $line, 'info', 1) ; +} + +sub print_rate { +    my (%data) = @_; +    my $line; +    foreach my $script (sort keys(%data)) { +	if ($data{$script}) { +            $line .= "%go%n %9".$script."%9 has been rated"; +        } else { +            $line .= "%ro%n %9".$script."%9 : Already rated this script"; +        } +    } +    print CLIENTCRAP draw_box('ScriptAssist', $line, 'rating', 1) ; +} + +sub print_ratings { +    my (%data) = @_; +    my @table; +    foreach my $script (sort {$data{$b}{rating}<=>$data{$a}{rating}} keys(%data)) { +	my @line; +	if (get_local_version($script)) { +	    push @line, "%go%n"; +	} else { +	    push @line, "%yo%n"; +	} +        push @line, "%9".$script."%9"; +	push @line, $data{$script}{rating}; +	push @line, "[".$data{$script}{votes}." votes]"; +	push @table, \@line; +    } +    print CLIENTCRAP draw_box('ScriptAssist', array2table(@table), 'ratings', 1) ; +} + +sub print_new { +    my ($list) = @_; +    my @table; +    foreach (sort {$list->{$b}{last_modified} cmp $list->{$a}{last_modified}} keys %$list) { +	my @line; +	my ($name) = get_names($_); +        if (get_local_version($name)) { +            push @line, "%go%n"; +        } else { +            push @line, "%yo%n"; +        } +	push @line, "%9".$name."%9"; +	push @line, $list->{$_}{last_modified}; +	push @table, \@line; +    } +    print CLIENTCRAP draw_box('ScriptAssist', array2table(@table), 'new scripts', 1) ; +} + +sub print_debug { +    my (%data) = @_; +    my $line; +    foreach my $script (sort keys %data) { +	$line .= "%ro%n %9".$script."%9 failed to load\n"; +	$line .= "  Make sure you have the following perl modules installed:\n"; +	foreach (sort keys %{$data{$script}}) { +	    if ( $data{$script}{$_}{installed} == 1 ) { +		$line .= "  %g->%n ".$_." (found)"; +	    } else { +		$line .= "  %r->%n ".$_." (not found)\n"; +		$line .= "     [This module is optional]\n" if $data{$script}{$_}{optional}; +		$line .= "     [Try /scriptassist cpan ".$_."]"; +	    } +	    $line .= "\n"; +	} +	print CLIENTCRAP draw_box('ScriptAssist', $line, 'debug', 1) ; +    } +} + +sub load_script { +    my ($script) = @_; +    Irssi::command('script load '.$script); +} + +sub print_install { +    my (%data) = @_; +    my $text; +    my ($crashed, @installed); +    foreach my $script (sort keys %data) { +	my $line; +	if ($data{$script}{installed} == 1) { +	    my $hacked; +	    if ($have_gpg && Irssi::settings_get_bool('scriptassist_use_gpg')) { +		if ($data{$script}{signed} >= 0) { +		    load_script($script) unless (lc($script) eq lc($IRSSI{name})); +		} else { +		    $hacked = 1; +		} +	    } else { +		load_script($script) unless (lc($script) eq lc($IRSSI{name})); +	    } +    	    if (get_local_version($script) && not lc($script) eq lc($IRSSI{name})) { +		$line .= "%go%n %9".$script."%9 installed\n"; +		push @installed, $script; +	    } elsif (lc($script) eq lc($IRSSI{name})) { +		$line .= "%yo%n %9".$script."%9 installed, please reload manually\n"; +	    } else { +    		$line .= "%Ro%n %9".$script."%9 fetched, but unable to load\n"; +		$crashed .= $script." " unless $hacked; +	    } +	    if ($have_gpg && Irssi::settings_get_bool('scriptassist_use_gpg')) { +		foreach (split /\n/, check_sig($data{$script})) { +		    $line .= "  ".$_."\n"; +		} +	    } +	} elsif ($data{$script}{installed} == -2) { +	    $line .= "%ro%n %9".$script."%9 already loaded, please try \"update\"\n"; +	} elsif ($data{$script}{installed} <= 0) { +	    $line .= "%ro%n %9".$script."%9 not installed\n"; +    	    foreach (split /\n/, check_sig($data{$script})) { +		$line .= "  ".$_."\n"; +	    } +	} else { +	    $line .= "%Ro%n %9".$script."%9 not found on server\n"; +	} +	$text .= $line; +    } +    # Inspect crashed scripts +    bg_do("debug ".$crashed) if $crashed; +    print CLIENTCRAP draw_box('ScriptAssist', $text, 'install', 1); +    list_sbitems(\@installed); +} + +sub list_sbitems { +    my ($scripts) = @_; +    my $text; +    foreach (@$scripts) { +	next unless exists $Irssi::Script::{"${_}::"}; +	next unless exists $Irssi::Script::{"${_}::"}{IRSSI}; +	my $header = $Irssi::Script::{"${_}::"}{IRSSI}; +	next unless $header->{sbitems}; +	$text .= '%9"'.$_.'"%9 provides the following statusbar item(s):'."\n"; +	$text .= '  ->'.$_."\n" foreach (split / /, $header->{sbitems}); +    } +    return unless $text; +    $text .= "\n"; +    $text .= "Enter '/statusbar window add <item>' to add an item."; +    print CLIENTCRAP draw_box('ScriptAssist', $text, 'sbitems', 1); +} + +sub check_sig { +    my ($sig) = @_; +    my $line; +    my %trust = ( -1 => 'undefined', +                   0 => 'never', +		   1 => 'marginal', +		   2 => 'fully', +		   3 => 'ultimate' +		 ); +    if ($sig->{signed} == 1) { +	$line .= "Signature found from ".$sig->{sig}{user}."\n"; +	$line .= "Timestamp  : ".$sig->{sig}{date}."\n"; +	$line .= "Fingerprint: ".$sig->{sig}{fingerprint}."\n"; +	$line .= "KeyID      : ".$sig->{sig}{keyid}."\n"; +	$line .= "Trust      : ".$trust{$sig->{sig}{trust}}."\n"; +    } elsif ($sig->{signed} == -1) { +	$line .= "%1Warning, unable to verify signature%n\n"; +    } elsif ($sig->{signed} == 0) { +	$line .= "%1No signature found%n\n" unless Irssi::settings_get_bool('scriptassist_install_unsigned_scripts'); +    } +    return $line; +} + +sub print_search { +    my ($query, %data) = @_; +    my $text; +    foreach (sort keys %data) { +	my $line; +	$line .= "%go%n" if $data{$_}{installed}; +	$line .= "%yo%n" if not $data{$_}{installed}; +	$line .= " %9".$_."%9 "; +	$line .= $data{$_}{desc}; +	$line =~ s/($query)/%U$1%U/gi; +	$line .= ' ('.$data{$_}{authors}.')'; +	$text .= $line." \n"; +    } +    print CLIENTCRAP draw_box('ScriptAssist', $text, 'search: '.$query, 1) ; +} + +sub print_update { +    my (%data) = @_; +    my $text; +    my @table; +    my $verbose = Irssi::settings_get_bool('scriptassist_update_verbose'); +    foreach (sort keys %data) { +	my $signed = 0; +	if ($data{$_}{installed} == 1) { +	    my $local = $data{$_}{local}; +	    my $remote = $data{$_}{remote}; +	    push @table, ['%yo%n', '%9'.$_.'%9', 'upgraded ('.$local.'->'.$remote.')']; +	    foreach (split /\n/, check_sig($data{$_})) { +		push @table, ['', '', $_]; +	    } +	    if (lc($_) eq lc($IRSSI{name})) { +		push @table, ['', '', "%R%9Please reload manually%9%n"]; +	    } else { +		load_script($_); +	    } +	} elsif ($data{$_}{installed} == 0 || $data{$_}{installed} == -1) { +	    push @table, ['%yo%n', '%9'.$_.'%9', 'not upgraded']; +            foreach (split /\n/, check_sig($data{$_})) { +		push @table, ['', '', $_]; +            } +	} elsif ($data{$_}{installed} == -2 && $verbose) { +	    my $local = $data{$_}{local}; +	    push @table, ['%go%n', '%9'.$_.'%9', 'already at the latest version ('.$local.')']; +    	} +    } +    $text = array2table(@table); +    print CLIENTCRAP draw_box('ScriptAssist', $text, 'update', 1) ; +} + +sub contact_author { +    my ($script) = @_; +    my ($sname, $plname, $pname) = get_names($script); +    return unless exists $Irssi::Script::{$pname}; +    my $header = $Irssi::Script::{$pname}{IRSSI}; +    if ($header && defined $header->{contact}) { +	my @ads = split(/ |,/, $header->{contact}); +	my $address = $ads[0]; +	$address .= '?subject='.$script; +	$address .= '_'.get_local_version($script) if defined get_local_version($script); +	call_openurl($address) if $address =~ /[\@:]/; +    } +} + +sub get_scripts { +    my $ua = LWP::UserAgent->new(env_proxy=>1, keep_alive=>1, timeout=>30); +    $ua->agent('ScriptAssist/'.2003020803); +    $ua->env_proxy(); +    my @mirrors = split(/ /, Irssi::settings_get_str('scriptassist_script_sources')); +    my %sites_db; +    my $not_modified = 0; +    my $fetched = 0; +    my @sources; +    my $error; +    foreach my $site (@mirrors) { +	my $request = HTTP::Request->new('GET', $site); +	if ($remote_db{timestamp}) { +	    $request->if_modified_since($remote_db{timestamp}); +	} +	my $response = $ua->request($request); +	if ($response->code == 304) { # HTTP_NOT_MODIFIED +	    $not_modified = 1; +	    next; +	} +	unless ($response->is_success) { +	    $error = join "\n", $response->status_line(), (grep / at .* line \d+/, split "\n", $response->content()), ''; +	    next; +	} +	$fetched = 1; +	my $data = $response->content(); +	my ($src, $type); +	if ($site =~ /(.*\/).+\.(.+)/) { +	    $src = $1; +	    $type = $2; +	} +	push @sources, $src; +	#my @header = ('name', 'contact', 'authors', 'description', 'version', 'modules', 'last_modified'); +	if ($type eq 'dmp') { +	    no strict 'vars'; +	    my $new_db = eval "$data"; +	    foreach (keys %$new_db) { +		if (defined $sites_db{script}{$_}) { +		    my $old = $sites_db{$_}{version}; +		    my $new = $new_db->{$_}{version}; +		    next if (compare_versions($old, $new) eq 'newer'); +		} +		#foreach my $key (@header) { +		foreach my $key (keys %{ $new_db->{$_} }) { +		    next unless defined $new_db->{$_}{$key}; +		    $sites_db{$_}{$key} = $new_db->{$_}{$key}; +		} +		$sites_db{$_}{source} = $src; +	    } +	} else { +	    die("Unknown script database type ($type).\n"); +	} +    } +    if ($fetched) { +	# Clean database +	foreach (keys %{$remote_db{db}}) { +	    foreach my $site (@sources) { +		if ($remote_db{db}{$_}{source} eq $site) { +		    delete $remote_db{db}{$_}; +		    last; +		} +	    } +	} +	$remote_db{db}{$_} = $sites_db{$_} foreach (keys %sites_db); +	$remote_db{timestamp} = time(); +    } elsif ($not_modified) { +	# nothing to do +    } else { +	die("No script database sources defined in /set scriptassist_script_sources\n") unless @mirrors; +	die("Fetching script database failed: $error") if $error; +	die("Unknown error while fetching script database\n"); +    } +    return $remote_db{db}; +} + +sub get_remote_version { +    my ($script, $database) = @_; +    my $plname = (get_names($script, $database))[1]; +    return $database->{$plname}{version}; +} + +sub get_local_version { +    my ($script) = @_; +    my $pname = (get_names($script))[2]; +    return unless exists $Irssi::Script::{$pname}; +    my $vref = $Irssi::Script::{$pname}{VERSION}; +    return $vref ? $$vref : undef; +} + +sub compare_versions { +    my ($ver1, $ver2) = @_; +    for ($ver1, $ver2) { +	$_ = "0:$_" unless /:/; +    } +    my @ver1 = split /[.:]/, $ver1; +    my @ver2 = split /[.:]/, $ver2; +    my $cmp = 0; +    ### Special thanks to Clemens Heidinger +    no warnings 'uninitialized'; +    $cmp ||= $ver1[$_] <=> $ver2[$_] || $ver1[$_] cmp $ver2[$_] for 0..scalar(@ver2); +    return 'newer' if $cmp == 1; +    return 'older' if $cmp == -1; +    return 'equal'; +} + +sub loaded_scripts { +    my @modules; +    foreach (sort grep(s/::$//, keys %Irssi::Script::)) { +	push @modules, $_; +    } +    return \@modules; +} + +sub check_scripts { +    my ($data) = @_; +    my %versions; +    foreach (@{loaded_scripts()}) { +	my ($sname) = get_names($_, $data); +	my $remote = get_remote_version($sname, $data); +	my $local = get_local_version($sname); +	my $state; +	if ($local && $remote) { +	    $state = compare_versions($local, $remote); +	} elsif ($local) { +	    $state = 'noversion'; +	    $remote = '/'; +	} else { +	    $state = 'noheader'; +	    $local = '/'; +	    $remote = '/'; +	} +	if ($state) { +	    $versions{$sname}{state} = $state; +	    $versions{$sname}{remote} = $remote; +	    $versions{$sname}{local} = $local; +	} +    } +    return \%versions; +} + +sub download_script { +    my ($script, $xml) = @_; +    my ($sname, $plname) = get_names($script, $xml); +    my %result; +    my $site = $xml->{$plname}{source}; +    $result{installed} = 0; +    $result{signed} = 0; +    my $dir = Irssi::get_irssi_dir(); +    my $ua = LWP::UserAgent->new(env_proxy => 1,keep_alive => 1,timeout => 30); +    $ua->agent('ScriptAssist/'.2003020803); +    my $request = HTTP::Request->new('GET', $site.'/scripts/'.$script.'.pl'); +    my $response = $ua->request($request); +    if ($response->is_success()) { +	my $file = $response->content(); +	mkdir $dir.'/scripts/' unless (-e $dir.'/scripts/'); +	open(my $F, '>', $dir.'/scripts/'.$plname.'.new'); +	print $F $file; +	close($F); +	if ($have_gpg && Irssi::settings_get_bool('scriptassist_use_gpg')) { +	    my $ua2 = LWP::UserAgent->new(env_proxy => 1,keep_alive => 1,timeout => 30); +	    $ua->agent('ScriptAssist/'.2003020803); +	    my $request2 = HTTP::Request->new('GET', $site.'/signatures/'.$plname.'.asc'); +	    my $response2 = $ua->request($request2); +	    if ($response2->is_success()) { +		my $sig_dir = $dir.'/scripts/signatures/'; +		mkdir $sig_dir unless (-e $sig_dir); +		open(my $S, '>', $sig_dir.$plname.'.asc'); +		my $file2 = $response2->content(); +		print $S $file2; +		close($S); +		my $sig; +		foreach (1..2) { +		    # FIXME gpg needs two rounds to load the key +		    my $gpg = new GnuPG(); +		    eval { +			$sig = $gpg->verify( file => $dir.'/scripts/'.$plname.'.new', signature => $sig_dir.$plname.'.asc' ); +		    }; +		} +		if (defined $sig->{user}) { +		    $result{installed} = 1; +		    $result{signed} = 1; +		    $result{sig}{$_} = $sig->{$_} foreach (keys %{$sig}); +		} else { +		    # Signature broken? +		    $result{installed} = 0; +		    $result{signed} = -1; +		} +	    } else { +		$result{signed} = 0; +		$result{installed} = -1; +		$result{installed} = 1 if Irssi::settings_get_bool('scriptassist_install_unsigned_scripts'); +	    } +	} else { +	    $result{signed} = 0; +	    $result{installed} = -1; +	    $result{installed} = 1 if Irssi::settings_get_bool('scriptassist_install_unsigned_scripts'); +	} +    } +    if ($result{installed}) { +	my $old_dir = "$dir/scripts/old/"; +	mkdir $old_dir unless (-e $old_dir); +	rename "$dir/scripts/$plname", "$old_dir/$plname.old" if -e "$dir/scripts/$plname"; +	rename "$dir/scripts/$plname.new", "$dir/scripts/$plname"; +    } +    return \%result; +} + +sub print_check { +    my (%data) = @_; +    my $text; +    my @table; +    foreach (sort keys %data) { +	my $state = $data{$_}{state}; +	my $remote = $data{$_}{remote}; +	my $local = $data{$_}{local}; +	if (Irssi::settings_get_bool('scriptassist_check_verbose')) { +	    push @table, ['%go%n', '%9'.$_.'%9', 'Up to date. ('.$local.')'] if $state eq 'equal'; +	} +	push @table, ['%mo%n', '%9'.$_.'%9', "No version information available on network."] if $state eq "noversion"; +	push @table, ['%mo%n', '%9'.$_.'%9', 'No header in script.'] if $state eq "noheader"; +	push @table, ['%bo%n', '%9'.$_.'%9', "Your version is newer (".$local."->".$remote.")"] if $state eq "newer"; +	push @table, ['%ro%n', '%9'.$_.'%9', "A new version is available (".$local."->".$remote.")"] if $state eq "older";; +    } +    $text = array2table(@table); +    print CLIENTCRAP draw_box('ScriptAssist', $text, 'check', 1) ; +} + +sub toggle_autorun { +    my ($script) = @_; +    my ($sname, $plname) = get_names($script); +    my $dir = Irssi::get_irssi_dir()."/scripts/"; +    mkdir $dir."autorun/" unless (-e $dir."autorun/"); +    return unless (-e $dir.$plname); +    if (-e $dir."/autorun/".$plname) { +	if (readlink($dir."/autorun/".$plname) eq "../".$plname) { +	    if (unlink($dir."/autorun/".$plname)) { +		print CLIENTCRAP "%R>>%n Autorun of ".$sname." disabled"; +	    } else { +		print CLIENTCRAP "%R>>%n Unable to delete link"; +	    } +	} else { +	    print CLIENTCRAP "%R>>%n ".$dir."/autorun/".$plname." is not a correct link"; +	} +    } else { +	if (symlink("../".$plname, $dir."/autorun/".$plname)) { +    	    print CLIENTCRAP "%R>>%n Autorun of ".$sname." enabled"; +	} else { +	    print CLIENTCRAP "%R>>%n Unable to create autorun link"; +	} +    } +} + +sub sig_script_error { +    my ($script, $msg) = @_; +    return unless Irssi::settings_get_bool('scriptassist_catch_script_errors'); +    if ($msg =~ /Can't locate (.*?)\.pm in \@INC \(\@INC contains:(.*?) at/) { +        my $module = $1; +        $module =~ s/\//::/g; +	missing_module($module); +    } +} + +sub missing_module { +    my ($module) = @_; +    my $text; +    $text .= "The perl module %9".$module."%9 is missing on your system.\n"; +    $text .= "Please ask your administrator about it.\n"; +    $text .= "You can also check CPAN via '/scriptassist cpan ".$module."'.\n"; +    print CLIENTCRAP &draw_box('ScriptAssist', $text, $module, 1); +} + +sub cmd_scripassist { +    my ($arg, $server, $witem) = @_; +    my @args = split(/ /, $arg); +    if ($args[0] eq 'help' || $args[0] eq '-h') { +	show_help(); +    } elsif ($args[0] eq 'check') { +	bg_do("check"); +    } elsif ($args[0] eq 'update') { +	shift @args; +	bg_do("update ".join(' ', @args)); +    } elsif ($args[0] eq 'search' && defined $args[1]) { +	shift @args; +	bg_do("search ".join(" ", @args)); +    } elsif ($args[0] eq 'install' && defined $args[1]) { +	shift @args; +	bg_do("install ".join(' ', @args)); +    } elsif ($args[0] eq 'contact' && defined $args[1]) { +	contact_author($args[1]); +    } elsif ($args[0] eq 'ratings' && defined $args[1]) { +	shift @args; +	bg_do("ratings ".join(' ', @args)); +    } elsif ($args[0] eq 'rate' && defined $args[1] && defined $args[2]) { +	shift @args; +	bg_do("rate ".join(' ', @args)) if ($args[2] >= 0 && $args[2] < 6); +    } elsif ($args[0] eq 'info' && defined $args[1]) { +	shift @args; +	bg_do("info ".join(' ', @args)); +    } elsif ($args[0] eq 'echo') { +	bg_do("echo"); +    } elsif ($args[0] eq 'top') { +	my $number = defined $args[1] ? $args[1] : 10; +	bg_do("top ".$number); +    } elsif ($args[0] eq 'cpan' && defined $args[1]) { +	call_openurl('http://search.cpan.org/search?mode=module&query='.$args[1]); +    } elsif ($args[0] eq 'autorun' && defined $args[1]) { +	toggle_autorun($args[1]); +    } elsif ($args[0] eq 'new') { +	my $number = defined $args[1] ? $args[1] : 5; +	bg_do("new ".$number); +    } +} + +sub cmd_help { +    my ($arg, $server, $witem) = @_; +    $arg =~ s/\s+$//; +    if ($arg =~ /^scriptassist/i) { +	show_help(); +    } +} + +sub sig_command_script_load { +    my ($script, $server, $witem) = @_; +    my ($sname, $plname, $pname, $xname) = get_names($script); +    if ( exists $Irssi::Script::{$pname} ) { +	if (my $code = "Irssi::Script::${pname}"->can('pre_unload')) { +	    print CLIENTCRAP "%R>>%n Triggering pre_unload function of $script..."; +	    $code->(); +	} +    } +} + +sub sig_default_command { +    my ($cmd, $server) = @_; +    return unless Irssi::settings_get_bool("scriptassist_check_unknown_commands"); +    bg_do('unknown '.$cmd); +} + +sub sig_complete { +    my ($list, $window, $word, $linestart, $want_space) = @_; +    return unless $linestart =~ /^.script(assist)? (install|rate|ratings|update|check|contact|info|autorun)/i; +    my @newlist; +    my $str = $word; +    foreach (@complist) { +	if ($_ =~ /^(\Q$str\E.*)?$/) { +	    push @newlist, $_; +	} +    } +    foreach (@{loaded_scripts()}) { +	push @newlist, $_ if /^(\Q$str\E.*)?$/; +    } +    push @$list, $_ foreach @newlist; +    Irssi::signal_stop(); +} + + +Irssi::settings_add_str($IRSSI{name}, 'scriptassist_script_sources', 'https://scripts.irssi.org/scripts.dmp'); +Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_cache_sources', 1); +Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_update_verbose', 1); +Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_check_verbose', 1); +Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_catch_script_errors', 1); + +Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_install_unsigned_scripts', 1); +Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_use_gpg', 1); +Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_integrate', 1); +Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_check_unknown_commands', 1); + +Irssi::signal_add_first("default command", 'sig_default_command'); +Irssi::signal_add_first('complete word', 'sig_complete'); +Irssi::signal_add_first('command script load', 'sig_command_script_load'); +Irssi::signal_add_first('command script unload', 'sig_command_script_load'); + +Irssi::signal_register({ 'script error' => [ 'Irssi::Script', 'string' ] }); +Irssi::signal_add_last('script error', 'sig_script_error'); + +Irssi::command_bind('scriptassist', 'cmd_scripassist'); +Irssi::command_bind('help', 'cmd_help'); + +Irssi::theme_register(['box_header', '%R,--[%n$*%R]%n', +'box_inside', '%R|%n $*', +'box_footer', '%R`--<%n$*%R>->%n', +]); + +foreach my $cmd ( ( 'check', +		    'install', +		    'update', +		    'contact', +		    'search', +#		    '-h', +		    'help', +#		    'ratings', +#		    'rate', +		    'info', +#		    'echo', +#		    'top', +		    'cpan', +		    'autorun', +		    'new' ) ) { +    Irssi::command_bind('scriptassist '.$cmd => sub { +			cmd_scripassist("$cmd ".$_[0], $_[1], $_[2]); }); +    if (Irssi::settings_get_bool('scriptassist_integrate')) { +	Irssi::command_bind('script '.$cmd => sub { +    			    cmd_scripassist("$cmd ".$_[0], $_[1], $_[2]); }); +    } +} + +print CLIENTCRAP '%B>>%n '.$IRSSI{name}.' '.$VERSION.' loaded: /scriptassist help for help'; diff --git a/.irssi/scripts/tmux_away.pl b/.irssi/scripts/tmux_away.pl new file mode 100644 index 0000000..36fb160 --- /dev/null +++ b/.irssi/scripts/tmux_away.pl @@ -0,0 +1,202 @@ +use Irssi; +use strict; +use FileHandle; + +use vars qw($VERSION %IRSSI); + +$VERSION = "2.1"; # e8934ed1ce04461 +%IRSSI = ( +    authors     => 'cdidier', +    name        => 'tmux_away', +    description => 'set (un)away if tmux session is attached/detached', +    license     => 'GPL v2', +    url         => 'http://cybione.org', +); + +# tmux_away irssi module +# +# Written by Colin Didier <cdidier@cybione.org> and heavily based on +# screen_away irssi module version 0.9.7.1 written by Andreas 'ads' Scherbaum +# <ads@ufp.de>. +# +# Updated by John C. Vernaleo <john@netpurgatory.com> to handle tmux with +# named sessions and other code cleanup and forked as version 2.0. +# +# usage: +# +# put this script into your autorun directory and/or load it with +#  /SCRIPT LOAD <name> +# +# there are 5 settings available: +# +# /set tmux_away_active ON/OFF/TOGGLE +# /set tmux_away_repeat <integer> +# /set tmux_away_grace <integer> +# /set tmux_away_message <string> +# /set tmux_away_window <string> +# /set tmux_away_nick <string> +# +# active means that you will be only set away/unaway, if this +#   flag is set, default is ON +# repeat is the number of seconds, after the script will check the +#   tmux session status again, default is 5 seconds +# grace is the number of seconds, to wait additionally, before +#   setting you away, default is disabled (0) +# message is the away message sent to the server, default: not here ... +# window is a window number or name, if set, the script will switch +#   to this window, if it sets you away, default is '1' +# nick is the new nick, if the script goes away +#   will only be used it not empty + + +# variables +my $timer_name = undef; +my $away_status = 0; +my %old_nicks = (); +my %away = (); + +# Register formats +Irssi::theme_register( +[ + 'tmux_away_crap', + '{line_start}{hilight ' . $IRSSI{'name'} . ':} $0' +]); + +# try to find out if we are running in a tmux session +# (see if $ENV{TMUX} is set) +if (!defined($ENV{TMUX})) { +  # just return, we will never be called again +  Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'tmux_away_crap', +    "no tmux session!"); +  return; +} + +my @args_env = split(',', $ENV{TMUX}); +my $tmux_socket = $args_env[0]; + +# register config variables +Irssi::settings_add_bool('misc', $IRSSI{'name'} . '_active', 1); +Irssi::settings_add_int('misc', $IRSSI{'name'} . '_repeat', 5); +Irssi::settings_add_int('misc', $IRSSI{'name'} . '_grace', 0); +Irssi::settings_add_str('misc', $IRSSI{'name'} . '_message', "not here..."); +Irssi::settings_add_str('misc', $IRSSI{'name'} . '_window', "1"); +Irssi::settings_add_str('misc', $IRSSI{'name'} . '_nick', ""); + + +# check, set or reset the away status +sub tmux_away { +    my ($immediate) = @_; +  my ($status, @res); + +  # only run, if activated +  if (Irssi::settings_get_bool($IRSSI{'name'} . '_active') != 1) { +    $away_status = 0; +  } else { +    if ($away_status == 0) { +      # display init message at first time +	my $grace = Irssi::settings_get_int($IRSSI{'name'} . '_grace'); +	$grace = ", $grace seconds grace" if $grace; +      Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'tmux_away_crap', +        "activating $IRSSI{'name'} (interval: " . Irssi::settings_get_int($IRSSI{'name'} . '_repeat') . " seconds$grace)"); +      $away_status = 2; +    } + +    # get actual tmux session status +    chomp(@res = `tmux -S '$tmux_socket' lsc 2>&1`); +    chomp(my $tmux_session = `tmux -S '$tmux_socket' display -p '#S' 2>/dev/null`); +    if ($res[0] =~ /^server not found/ || $? >> 8) { +      die "error getting tmux session status."; +    } +    $status = 1; # away, assumes the session is detached +    foreach (@res) { +      my @args_st = split(' '); +      if ($args_st[1] eq $tmux_session) { +        $status = 2; # unaway +      } +    } + +    # unaway -> away +    if ($status == 1 and $away_status != 1) { +	if (my $grace = Irssi::settings_get_int($IRSSI{'name'} . '_grace')) { +	    if (!$immediate) { +		Irssi::timeout_add_once($grace * 1000, 'tmux_away', '1'); +		Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'tmux_away_crap', +				   "(in grace for away: $grace seconds)"); +		return 1; +	    } +	} +      if (length(Irssi::settings_get_str($IRSSI{'name'} . '_window')) > 0) { +        # if length of window is greater then 0, make this window active +        Irssi::command('window goto ' . Irssi::settings_get_str($IRSSI{'name'} . '_window')); +      } +      Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'tmux_away_crap', "Set away"); +      my $message = Irssi::settings_get_str($IRSSI{'name'} . '_message'); +      if (length($message) == 0) { +        # we have to set a message or we wouldnt go away +        $message = "not here ..."; +      } +      foreach (Irssi::servers()) { +        if (!$_->{usermode_away}) { +	  # user isn't yet away +	  $away{$_->{'tag'}} = 0; +	  $_->command("^AWAY " . ($_->{chat_type} ne 'SILC' ? "-one " : "") . "$message"); +	  if ($_->{chat_type} ne 'XMPP' and length(Irssi::settings_get_str($IRSSI{'name'} . '_nick')) > 0) { +            # only change if actual nick isn't already the away nick +            if (Irssi::settings_get_str($IRSSI{'name'} . '_nick') ne $_->{nick}) { +              # keep old nick +              $old_nicks{$_->{'tag'}} = $_->{nick}; +              # set new nick +              $_->command("NICK " . Irssi::settings_get_str($IRSSI{'name'} . '_nick')); +            } +          } +        } else { +          # user is already away, remember this +          $away{$_->{'tag'}} = 1; +        } +      } +      $away_status = $status; + +    # away -> unaway +    } elsif ($status == 2 and $away_status != 2) { +	if (my $grace = Irssi::settings_get_int($IRSSI{'name'} . '_grace')) { +	    if (!$immediate) { +		Irssi::timeout_add_once($grace * 1000, 'tmux_away', '1'); +		Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'tmux_away_crap', +				   "(in grace for unaway: $grace seconds)"); +		return 1; +	    } +	} +      # unset away +      Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'tmux_away_crap', "Reset away"); +      foreach (Irssi::servers()) { +        if ($away{$_->{'tag'}} == 1) { +          # user was already away, don't reset away +          $away{$_->{'tag'}} = 0; +          next; +        } +        $_->command("^AWAY" . (($_->{chat_type} ne 'SILC') ? " -one" : "")) if ($_->{usermode_away}); +        if ($_->{chat_type} ne 'XMPP' and defined($old_nicks{$_->{'tag'}}) and length($old_nicks{$_->{'tag'}}) > 0) { +          # set old nick +          $_->command("NICK " . $old_nicks{$_->{'tag'}}); +          $old_nicks{$_->{'tag'}} = ""; +        } +      } +      $away_status = $status; +    } elsif ($immediate) { +	Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'tmux_away_crap', +				   "in grace aborted"); +    } +  } +  # but everytimes install a new timer +  register_tmux_away_timer(); +  return 0; +} + +# remove old timer and install a new one +sub register_tmux_away_timer { +  # add new timer with new timeout (maybe the timeout has been changed) +  Irssi::timeout_add_once(Irssi::settings_get_int($IRSSI{'name'} . '_repeat') * 1000, 'tmux_away', ''); +} + +# init process +tmux_away(); diff --git a/.irssi/scripts/trackbar.pl b/.irssi/scripts/trackbar.pl new file mode 100644 index 0000000..7ac9965 --- /dev/null +++ b/.irssi/scripts/trackbar.pl @@ -0,0 +1,579 @@ +## trackbar.pl +# +# This little script will do just one thing: it will draw a line each time you +# switch away from a window. This way, you always know just upto where you've +# been reading that window :) It also removes the previous drawn line, so you +# don't see double lines. +# +#  redraw trackbar only works on irssi 0.8.17 or higher. +# +## + +## Usage: +# +#     The script works right out of the box, but if you want you can change +#     the working by /set'ing the following variables: +# +#    Setting:     trackbar_style +#    Description: This setting will be the color of your trackbar line. +#                 By default the value will be '%K', only Irssi color +#                 formats are allowed. If you don't know the color formats +#                 by heart, you can take a look at the formats documentation. +#                 You will find the proper docs on http://www.irssi.org/docs. +# +#    Setting:     trackbar_string +#    Description: This is the string that your line will display. This can +#                 be multiple characters or just one. For example: '~-~-' +#                 The default setting is '-'. +#                 Here are some unicode characters you can try: +#                     "───" => U+2500 => a line +#                     "═══" => U+2550 => a double line +#                     "━━━" => U+2501 => a wide line +#                     "▭  " => U+25ad => a white rectangle +# +#    Setting:     trackbar_use_status_window +#    Description: If this setting is set to OFF, Irssi won't print a trackbar +#                 in the statuswindow +# +#    Setting:     trackbar_ignore_windows +#    Description: A list of windows where no trackbar should be printed +# +#    Setting:     trackbar_print_timestamp +#    Description: If this setting is set to ON, Irssi will print the formatted +#                 timestamp in front of the trackbar. +# +#    Setting:     trackbar_require_seen +#    Description: Only clear the trackbar if it has been scrolled to. +# +#     /mark is a command that will redraw the line at the bottom. +# +#    Command:     /trackbar, /trackbar goto +#    Description: Jump to where the trackbar is, to pick up reading +# +#    Command:     /trackbar keep +#    Description: Keep this window's trackbar where it is the next time +#                 you switch windows (then this flag is cleared again) +# +#    Command:     /mark, /trackbar mark +#    Description: Remove the old trackbar and mark the bottom of this +#                 window with a new trackbar +# +#    Command:     /trackbar markvisible +#    Description: Like mark for all visible windows +# +#    Command:     /trackbar markall +#    Description: Like mark for all windows +# +#    Command:     /trackbar remove +#    Description: Remove this window's trackbar +# +#    Command:     /trackbar removeall +#    Description: Remove all windows' trackbars +# +#    Command:     /trackbar redraw +#    Description: Force redraw of trackbars +# +## + +## +# +# For bugreports and other improvements contact one of the authors. +# +#    This program is free software; you can redistribute it and/or modify +#    it under the terms of the GNU General Public License as published by +#    the Free Software Foundation; either version 2 of the License, or +#    (at your option) any later version. +# +#    This program is distributed in the hope that it will be useful, +#    but WITHOUT ANY WARRANTY; without even the implied warranty of +#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#    GNU General Public License for more details. +# +#    You should have received a copy of the GNU General Public License +#    along with this script; if not, write to the Free Software +#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA +# +## + +use strict; +use warnings; +use vars qw($VERSION %IRSSI); + +$VERSION = "2.5"; # 56e983314dc1b85 + +%IRSSI = ( +    authors     => "Peter 'kinlo' Leurs, Uwe Dudenhoeffer, " . +                   "Michiel Holtkamp, Nico R. Wohlgemuth, " . +                   "Geert Hauwaerts", +    contact     => 'peter@pfoe.be', +    patchers    => 'Johan Kiviniemi (UTF-8), Uwe Dudenhoeffer (on-upgrade-remove-line)', +    name        => 'trackbar', +    description => 'Shows a bar where you have last read a window.', +    license     => 'GNU General Public License', +    url         => 'http://www.pfoe.be/~peter/trackbar/', +    commands    => 'trackbar', +); + +## Comments and remarks. +# +# This script uses settings. +# Use /SET  to change the value or /TOGGLE to switch it on or off. +# +# +#    Tip:     The command 'trackbar' is very usefull if you bind that to a key, +#             so you can easily jump to the trackbar. Please see 'help bind' for +#             more information about keybindings in Irssi. +# +#    Command: /BIND meta2-P key F1 +#             /BIND F1 command trackbar +# +## + +## Bugfixes and new items in this rewrite. +# +# * Remove all the trackbars before upgrading. +# * New setting trackbar_use_status_window to control the statuswindow trackbar. +# * New setting trackbar_print_timestamp to print a timestamp or not. +# * New command 'trackbar' to scroll up to the trackbar. +# * When resizing your terminal, Irssi will update all the trackbars to the new size. +# * When changing trackbar settings, change all the trackbars to the new settings. +# * New command 'trackbar mark' to draw a new trackbar (The old '/mark'). +# * New command 'trackbar markall' to draw a new trackbar in each window. +# * New command 'trackbar remove' to remove the trackbar from the current window. +# * New command 'trackbar removeall' to remove all the trackbars. +# * Don't draw a trackbar in empty windows. +# * Added a version check to prevent Irssi redraw errors. +# * Fixed a bookmark NULL versus 0 bug. +# * Fixed a remove-line bug in Uwe Dudenhoeffer his patch. +# * New command 'help trackbar' to display the trackbar commands. +# * Fixed an Irssi startup bug, now processing each auto-created window. +# +## + +## Known bugs and the todolist. +# +#    Todo: * Instead of drawing a line, invert the line. +# +## + +## Authors: +# +#   - Main maintainer & author: Peter 'kinlo' Leurs +#   - Many thanks to Timo 'cras' Sirainen for placing me on my way +#   - on-upgrade-remove-line patch by Uwe Dudenhoeffer +#   - trackbar resizing by Michiel Holtkamp (02 Jul 2012) +#   - scroll to trackbar, window excludes, and timestamp options by Nico R. +#     Wohlgemuth (22 Sep 2012) +# +## + +## Version history: +# +#  2.5: - merge back on scripts.irssi.org +#       - fix /trackbar redraw broken in 2.4 +#       - fix legacy encodings +#       - add workaround for irssi issue #271 +#  2.4: - add support for horizontal splits +#  2.3: - add some features for seen tracking using other scripts +#  2.0: - big rewrite based on 1.4 +#         * removed /tb, you can have it with /alias tb trackbar if you want +#         * subcommand and settings changes: +#              /trackbar vmark  => /trackbar markvisible +#              /trackbar scroll => /trackbar goto (or just /trackbar) +#              /trackbar help   => /help trackbar +#              /set trackbar_hide_windows => /set trackbar_ignore_windows +#              /set trackbar_timestamp    => /set trackbar_print_timestamp +#         * magic line strings were removed, just paste the unicode you want! +#         * trackbar_timestamp_styled is not currently supported +#  1.9: - add version guard +#  1.8: - sub draw_bar +#  1.7: - Added /tb scroll, trackbar_hide_windows, trackbar_timestamp_timestamp +#         and trackbar_timestamp_styled +#  1.6: - Work around Irssi resize bug, please do /upgrade! (see below) +#  1.5: - Resize trackbars in all windows when terminal is resized +#  1.4: - Changed our's by my's so the irssi script header is valid +#       - Removed utf-8 support.  In theory, the script should work w/o any +#         problems for utf-8, just set trackbar_string to a valid utf-8 character +#         and everything *should* work.  However, this script is being plagued by +#         irssi internal bugs.  The function Irssi::settings_get_str does NOT handle +#         unicode strings properly, hence you will notice problems when setting the bar +#         to a unicode char.  For changing your bar to utf-8 symbols, read the line sub. +#  1.3: - Upgrade now removes the trackbars. +#       - Some code cleanups, other defaults +#       - /mark sets the line to the bottom +#  1.2: - Support for utf-8 +#       - How the bar looks can now be configured with trackbar_string +#         and trackbar_style +#  1.1: - Fixed bug when closing window +#  1.0: - Initial release +# +## + +use Irssi; +use Irssi::TextUI; +use Encode; + +use POSIX qw(strftime); + +sub cmd_help { +    my ($args) = @_; +    if ($args =~ /^trackbar *$/i) { +        print CLIENTCRAP <<HELP +%9Syntax:%9 + +TRACKBAR +TRACKBAR GOTO +TRACKBAR KEEP +TRACKBAR MARK +TRACKBAR MARKVISIBLE +TRACKBAR MARKALL +TRACKBAR REMOVE +TRACKBAR REMOVEALL +TRACKBAR REDRAW + +%9Parameters:%9 + +    GOTO:        Jump to where the trackbar is, to pick up reading +    KEEP:        Keep this window's trackbar where it is the next time +                 you switch windows (then this flag is cleared again) +    MARK:        Remove the old trackbar and mark the bottom of this +                 window with a new trackbar +    MARKVISIBLE: Like mark for all visible windows +    MARKALL:     Like mark for all windows +    REMOVE:      Remove this window's trackbar +    REMOVEALL:   Remove all windows' trackbars +    REDRAW:      Force redraw of trackbars + +%9Description:%9 + +    Manage a trackbar. Without arguments, it will scroll up to the trackbar. + +%9Examples:%9 + +    /TRACKBAR MARK +    /TRACKBAR REMOVE +HELP +    } +} + +Irssi::theme_register([ +    'trackbar_loaded', '%R>>%n %_Scriptinfo:%_ Loaded $0 version $1 by $2.', +    'trackbar_wrong_version', '%R>>%n %_Trackbar:%_ Please upgrade your client to 0.8.17 or above if you would like to use this feature of trackbar.', +    'trackbar_all_removed', '%R>>%n %_Trackbar:%_ All the trackbars have been removed.', +    'trackbar_not_found', '%R>>%n %_Trackbar:%_ No trackbar found in this window.', +]); + +my $old_irssi = Irssi::version < 20140701; +sub check_version { +    if ($old_irssi) { +        Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_wrong_version'); +        return; +    } else { +        return 1; +    } +} + +sub is_utf8 { +    lc Irssi::settings_get_str('term_charset') eq 'utf-8' +} + +my (%config, %keep_trackbar, %unseen_trackbar); + +sub remove_one_trackbar { +    my $win = shift; +    my $view = shift || $win->view; +    my $line = $view->get_bookmark('trackbar'); +    if (defined $line) { +        my $bottom = $view->{bottom}; +        $view->remove_line($line); +        $win->command('^scrollback end') if $bottom && !$win->view->{bottom}; +        $view->redraw; +    } +} + +sub add_one_trackbar { +    my $win = shift; +    my $view = shift || $win->view; +    $win->print(line($win->{width}), MSGLEVEL_NEVER); +    $view->set_bookmark_bottom('trackbar'); +    $unseen_trackbar{ $win->{_irssi} } = 1; +    Irssi::signal_emit("window trackbar added", $win); +    $view->redraw; +} + +sub update_one_trackbar { +    my $win = shift; +    my $view = shift || $win->view; +    my $force = shift; +    my $ignored = win_ignored($win, $view); +    remove_one_trackbar($win, $view) +	if $force || !defined $force || !$ignored; +    add_one_trackbar($win, $view) +	if $force || !$ignored; +} + +sub win_ignored { +    my $win = shift; +    my $view = shift || $win->view; +    return 1 unless $view->{buffer}{lines_count}; +    return 1 if $win->{name} eq '(status)' && !$config{use_status_window}; +    no warnings 'uninitialized'; +    return 1 if grep { $win->{name} eq $_ || $win->{refnum} eq $_ +			   || $win->get_active_name eq $_ } @{ $config{ignore_windows} }; +    return 0; +} + +sub sig_window_changed { +    my ($newwindow, $oldwindow) = @_; +    return unless $oldwindow; +    redraw_one_trackbar($newwindow) unless $old_irssi; +    trackbar_update_seen($newwindow); +    return if delete $keep_trackbar{ $oldwindow->{_irssi} }; +    trackbar_update_seen($oldwindow); +    return if $config{require_seen} && $unseen_trackbar{ $oldwindow->{_irssi } }; +    update_one_trackbar($oldwindow, undef, 0); +} + +sub trackbar_update_seen { +    my $win = shift; +    return unless $win; +    return unless $unseen_trackbar{ $win->{_irssi} }; + +    my $view = $win->view; +    my $line = $view->get_bookmark('trackbar'); +    unless ($line) { +        delete $unseen_trackbar{ $win->{_irssi} }; +        Irssi::signal_emit("window trackbar seen", $win); +        return; +    } +    my $startline = $view->{startline}; +    return unless $startline; + +    if ($startline->{info}{time} < $line->{info}{time} +            || $startline->{_irssi} == $line->{_irssi}) { +        delete $unseen_trackbar{ $win->{_irssi} }; +        Irssi::signal_emit("window trackbar seen", $win); +    } +} + +sub screen_length; +{ local $@; +  eval { require Text::CharWidth; }; +  unless ($@) { +      *screen_length = sub { Text::CharWidth::mbswidth($_[0]) }; +  } +  else { +      *screen_length = sub { +          my $temp = shift; +          Encode::_utf8_on($temp) if is_utf8(); +          length($temp) +      }; +  } +} + +{ my %strip_table = ( +    (map { $_ => '' } (split //, '04261537' .  'kbgcrmyw' . 'KBGCRMYW' . 'U9_8I:|FnN>#[' . 'pP')), +    (map { $_ => $_ } (split //, '{}%')), +   ); +  sub c_length { +      my $o = Irssi::strip_codes($_[0]); +      $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; +      screen_length($o) +  } +} + +sub line { +    my ($width, $time)  = @_; +    my $string = $config{string}; +    $string = ' ' unless length $string; +    $time ||= time; + +    Encode::_utf8_on($string) if is_utf8(); +    my $length = c_length($string); + +    my $format = ''; +    if ($config{print_timestamp}) { +        $format = $config{timestamp_str}; +        $format =~ y/%/\01/; +        $format =~ s/\01\01/%/g; +        $format = strftime($format, localtime $time); +        $format =~ y/\01/%/; +    } + +    my $times = $width / $length; +    $times += 1 if $times != int $times; +    my $style = "$config{style}"; +    Encode::_utf8_on($style) if is_utf8(); +    $format .= $style; +    $width -= c_length($format); +    $string x= $times; +    chop $string while length $string && c_length($string) > $width; +    return $format . $string; +} + +sub remove_all_trackbars { +    for my $window (Irssi::windows) { +        next unless ref $window; +        remove_one_trackbar($window); +    } +} + +sub UNLOAD { +    remove_all_trackbars(); +} + +sub redraw_one_trackbar { +    my $win = shift; +    my $view = $win->view; +    my $line = $view->get_bookmark('trackbar'); +    return unless $line; +    my $bottom = $view->{bottom}; +    $win->print_after($line, MSGLEVEL_NEVER, line($win->{width}, $line->{info}{time}), +		      $line->{info}{time}); +    $view->set_bookmark('trackbar', $win->last_line_insert); +    $view->remove_line($line); +    $win->command('^scrollback end') if $bottom && !$win->view->{bottom}; +    $view->redraw; +} + +sub redraw_trackbars { +    return unless check_version(); +    for my $win (Irssi::windows) { +        next unless ref $win; +        redraw_one_trackbar($win); +    } +} + +sub goto_trackbar { +    my $win = Irssi::active_win; +    my $line = $win->view->get_bookmark('trackbar'); + +    if ($line) { +        $win->command("scrollback goto ". strftime("%d %H:%M:%S", localtime($line->{info}{time}))); +    } else { +        $win->printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_not_found'); +    } +} + +sub cmd_mark { +    update_one_trackbar(Irssi::active_win, undef, 1); +} + +sub cmd_markall { +    for my $window (Irssi::windows) { +        next unless ref $window; +        update_one_trackbar($window); +    } +} + +sub signal_stop { +    Irssi::signal_stop; +} + +sub cmd_markvisible { +    my @wins = Irssi::windows; +    my $awin = +        my $bwin = Irssi::active_win; +    my $awin_counter = 0; +    Irssi::signal_add_priority('window changed' => 'signal_stop', -99); +    do { +        Irssi::active_win->command('window up'); +        $awin = Irssi::active_win; +        update_one_trackbar($awin); +        ++$awin_counter; +    } until ($awin->{refnum} == $bwin->{refnum} || $awin_counter >= @wins); +    Irssi::signal_remove('window changed' => 'signal_stop'); +} + +sub cmd_trackbar_remove_one { +    remove_one_trackbar(Irssi::active_win); +} + +sub cmd_remove_all_trackbars { +    remove_all_trackbars(); +    Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_all_removed'); +} + +sub cmd_keep_once { +    $keep_trackbar{ Irssi::active_win->{_irssi} } = 1; +} + +sub trackbar_runsub { +    my ($data, $server, $item) = @_; +    $data =~ s/\s+$//g; + +    if ($data) { +        Irssi::command_runsub('trackbar', $data, $server, $item); +    } else { +        goto_trackbar(); +    } +} + +sub update_config { +    my $was_status_window = $config{use_status_window}; +    $config{style} = Irssi::settings_get_str('trackbar_style'); +    $config{string} = Irssi::settings_get_str('trackbar_string'); +    $config{require_seen} = Irssi::settings_get_bool('trackbar_require_seen'); +    $config{ignore_windows} = [ split /[,\s]+/, Irssi::settings_get_str('trackbar_ignore_windows') ]; +    $config{use_status_window} = Irssi::settings_get_bool('trackbar_use_status_window'); +    $config{print_timestamp} = Irssi::settings_get_bool('trackbar_print_timestamp'); +    if (defined $was_status_window && $was_status_window != $config{use_status_window}) { +        if (my $swin = Irssi::window_find_name('(status)')) { +            if ($config{use_status_window}) { +                update_one_trackbar($swin); +            } +            else { +                remove_one_trackbar($swin); +            } +        } +    } +    if ($config{print_timestamp}) { +        my $ts_format = Irssi::settings_get_str('timestamp_format'); +        my $ts_theme = Irssi::current_theme->get_format('fe-common/core', 'timestamp'); +        my $render_str = Irssi::current_theme->format_expand($ts_theme); +        (my $ts_escaped = $ts_format) =~ s/([%\$])/$1$1/g; +        $render_str =~ s/(?|\$(.)(?!\w)|\$\{(\w+)\})/$1 eq 'Z' ? $ts_escaped : $1/ge; +        $config{timestamp_str} = $render_str; +    } +    redraw_trackbars() unless $old_irssi; +} + +Irssi::settings_add_str('trackbar', 'trackbar_string', is_utf8() ? "\x{2500}" : '-'); +Irssi::settings_add_str('trackbar', 'trackbar_style', '%K'); +Irssi::settings_add_str('trackbar', 'trackbar_ignore_windows', ''); +Irssi::settings_add_bool('trackbar', 'trackbar_use_status_window', 1); +Irssi::settings_add_bool('trackbar', 'trackbar_print_timestamp', 0); +Irssi::settings_add_bool('trackbar', 'trackbar_require_seen', 0); + +update_config(); + +Irssi::signal_add_last( 'mainwindow resized' => 'redraw_trackbars') +    unless $old_irssi; + +Irssi::signal_register({'window trackbar added' => [qw/Irssi::UI::Window/]}); +Irssi::signal_register({'window trackbar seen' => [qw/Irssi::UI::Window/]}); +Irssi::signal_register({'gui page scrolled' => [qw/Irssi::UI::Window/]}); +Irssi::signal_add_last('gui page scrolled' => 'trackbar_update_seen'); + +Irssi::signal_add('setup changed' => 'update_config'); +Irssi::signal_add_priority('session save' => 'remove_all_trackbars', Irssi::SIGNAL_PRIORITY_HIGH-1); + +Irssi::signal_add('window changed' => 'sig_window_changed'); + +Irssi::command_bind('trackbar goto'      => 'goto_trackbar'); +Irssi::command_bind('trackbar keep'      => 'cmd_keep_once'); +Irssi::command_bind('trackbar mark'      => 'cmd_mark'); +Irssi::command_bind('trackbar markvisible' => 'cmd_markvisible'); +Irssi::command_bind('trackbar markall'   => 'cmd_markall'); +Irssi::command_bind('trackbar remove'    => 'cmd_trackbar_remove_one'); +Irssi::command_bind('trackbar removeall' => 'cmd_remove_all_trackbars'); +Irssi::command_bind('trackbar redraw'    => 'redraw_trackbars'); +Irssi::command_bind('trackbar'           => 'trackbar_runsub'); +Irssi::command_bind('mark'               => 'cmd_mark'); +Irssi::command_bind_last('help' => 'cmd_help'); + +Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_loaded', $IRSSI{name}, $VERSION, $IRSSI{authors}); + +# workaround for issue #271 +{ package Irssi::Nick }  | 
