diff options
Diffstat (limited to 'scripts/query.pl')
-rw-r--r-- | scripts/query.pl | 593 |
1 files changed, 593 insertions, 0 deletions
diff --git a/scripts/query.pl b/scripts/query.pl new file mode 100644 index 0000000..7fc13fa --- /dev/null +++ b/scripts/query.pl @@ -0,0 +1,593 @@ +# +# Copyright (C) 2001-2021 by Peder Stray <peder.stray@gmail.com> +# + +use strict; +use Irssi 20020428.1608; + +use Text::Abbrev; +use POSIX; + +use vars qw{$VERSION %IRSSI}; +($VERSION) = '$Revision: 1.26.1 $' =~ / (\d+(\.\d+)+) /; +%IRSSI = ( + name => 'query', + authors => 'Peder Stray', + contact => 'peder.stray@gmail.com', + url => 'https://github.com/pstray/irssi-query', + license => 'GPL', + description => 'Give you more control over when to jump to query windows and when to just tell you one has been created. Enhanced autoclose.', + ); + +use vars qw(%state); +*state = \%Query::state; # used for tracking idletime and state + +my($own); +my(%defaults); # used for storing defaults +my($query_opts) = {}; # stores option abbrevs + +sub load_defaults { + my $file = Irssi::get_irssi_dir."/query"; + local *FILE; + + %defaults = (); + open FILE, "<", $file; + while (<FILE>) { + my($mask,$maxage,$immortal) = split; + $defaults{$mask}{maxage} = $maxage; + $defaults{$mask}{immortal} = $immortal; + } + close FILE; +} + +sub save_defaults { + my $file = Irssi::get_irssi_dir."/query"; + local *FILE; + + open FILE, ">", $file; + for (keys %defaults) { + my $d = $defaults{$_}; + print FILE join("\t", $_, + exists $d->{maxage} ? $d->{maxage} : -1, + exists $d->{immortal} ? $d->{immortal} : -1, + ), "\n"; + } + close FILE; +} + +sub sec2str { + my($sec) = @_; + my($ret); + use integer; + + $ret = ($sec%60)."s "; + $sec /= 60; + + $ret = ($sec%60)."m ".$ret; + $sec /= 60; + + $ret = ($sec%24)."h ".$ret; + $sec /= 24; + + $ret = $sec."d ".$ret; + + $ret =~ s/\b0[dhms] //g; + $ret =~ s/ $//; + + return $ret; +} + +sub str2sec { + my($str) = @_; + + for ($str) { + s/\s+//g; + s/d/*24h/g; + s/h/*60m/g; + s/m/*60s/g; + s/s/+/g; + s/\+$//; + } + + if ($str =~ /^[0-9*+]+$/) { + $str = eval $str; + } + else { + $str = 0; + } + + return $str; +} + +sub set_defaults { + my($serv,$nick,$address) = @_; + my $tag = lc $serv->{tag}; + + return unless $address; + $state{$tag}{$nick}{address} = $address; + + for my $mask (sort {userhost_cmp($serv,$a,$b)}keys %defaults) { + if ($serv->mask_match_address($mask, $nick, $address)) { + for my $key (keys %{$defaults{$mask}}) { + $state{$tag}{$nick}{$key} = $defaults{$mask}{$key} + if $defaults{$mask}{$key} >= 0; + } + } + } +} + +sub time2str { + my($time) = @_; + return strftime("%c", localtime $time); +} + +sub userhost_cmp { + my($serv, $am, $bm) = @_; + my($an,$aa) = split "!", $am; + my($bn,$ba) = split "!", $bm; + my($t1,$t2); + + $t1 = $serv->mask_match_address($bm, $an, $aa); + $t2 = $serv->mask_match_address($am, $bn, $ba); + + return $t1 - $t2 if $t1 || $t2; + + $an = $bn = '*'; + $am = "$an!$aa"; + $bm = "$bn!$ba"; + + $t1 = $serv->mask_match_address($bm, $an, $aa); + $t2 = $serv->mask_match_address($am, $bn, $ba); + + return $t1 - $t2 if $t1 || $t2; + + for ($am, $bm, $aa, $ba) { + s/(\*!)?[^*]*@/$1*/; + } + + $t1 = $serv->mask_match_address($bm, $an, $aa); + $t2 = $serv->mask_match_address($am, $bn, $ba); + + return $t1 - $t2 if $t1 || $t2; + + return 0; + +} + +sub sig_message_own_private { + my($server,$msg,$nick,$orig_target) = @_; + $own = $nick; +} + +sub sig_message_private { + my($server,$msg,$nick,$addr) = @_; + undef $own; +} + +sub sig_print_message { + my($dest, $text, $strip) = @_; + + return unless $dest->{level} & MSGLEVEL_MSGS; + + my $server = $dest->{server}; + + return unless $server; + + my $witem = $server->window_item_find($dest->{target}); + my $tag = lc $server->{tag}; + + return unless $witem->{type} eq 'QUERY'; + + $state{$tag}{$witem->{name}}{time} = time; +} + +sub sig_query_address_changed { + my($query) = @_; + + set_defaults($query->{server}, $query->{name}, $query->{address}); + +} + +sub sig_query_created { + my ($query, $auto) = @_; + my $qwin = $query->window(); + my $awin = Irssi::active_win(); + + my $serv = $query->{server}; + my $nick = $query->{name}; + my $tag = lc $query->{server_tag}; + + if ($auto && $qwin->{refnum} != $awin->{refnum}) { + if ($own eq $query->{name}) { + if (Irssi::settings_get_bool('query_autojump_own')) { + $qwin->set_active(); + } else { + $awin->printformat(MSGLEVEL_CLIENTCRAP, 'query_created', + $nick, $query->{server_tag}, + $qwin->{refnum}) + if Irssi::settings_get_bool('query_noisy'); + } + } else { + if (Irssi::settings_get_bool('query_autojump')) { + $qwin->set_active(); + } else { + $awin->printformat(MSGLEVEL_CLIENTCRAP, 'query_created', + $nick, $query->{server_tag}, + $qwin->{refnum}) + if Irssi::settings_get_bool('query_noisy'); + } + } + } + undef $own; + + $state{$tag}{$nick} = { time => time }; + + if (ref($serv) eq 'Irssi::Irc::Server') { + $serv->redirect_event('userhost', 1, ":$nick", -1, undef, + { + "event 302" => "redir query userhost", + "" => "event empty", + }); + $serv->send_raw("USERHOST :$nick"); + } +} + +sub sig_query_destroyed { + my($query) = @_; + + delete $state{lc $query->{server_tag}}{$query->{name}}; +} + +sub sig_query_nick_changed { + my($query,$old_nick) = @_; + my($tag) = lc $query->{server_tag}; + + $state{$tag}{$query->{name}} = delete $state{$tag}{$old_nick}; +} + +sub sig_redir_query_userhost { + my($serv,$data) = @_; + + $data =~ s/^\S*\s*://; + for (split " ", $data) { + if (/([^=*]+)\*?=.(.+)/) { + set_defaults($serv, $1, $2); + } + } +} + +sub sig_session_restore { + open STATE, "<", sprintf "%s/query.state", Irssi::get_irssi_dir; + %state = (); # only needed if bound as command + while (<STATE>) { + chomp; + my($tag,$nick,%data) = split "\t"; + for my $key (keys %data) { + $state{lc $tag}{$nick}{$key} ||= $data{$key}; + } + } + close STATE; +} + +sub sig_session_save { + open STATE, ">", sprintf "%s/query.state", Irssi::get_irssi_dir; + for my $tag (keys %state) { + for my $nick (keys %{$state{$tag}}) { + print STATE join("\t",$tag,$nick,%{$state{$tag}{$nick}}), "\n"; + } + } + close STATE; +} + +sub check_queries { + my(@queries) = Irssi::queries; + + my($defmax) = Irssi::settings_get_time('query_autoclose')/1000; + my($minage) = Irssi::settings_get_time('query_autoclose_grace')/1000; + my($win) = Irssi::active_win; + + for my $query (@queries) { + my $tag = lc $query->{server_tag}; + my $name = $query->{name}; + my $state = $state{$tag}{$name}; + + my $age = time - $state->{time}; + my $maxage = $defmax; + + $maxage = $state->{maxage} if defined $state->{maxage}; + + # skip the ones we have marked as immortal + next if $state->{immortal}; + + # maxage = 0 means we have disabled autoclose + next unless $maxage; + + # not old enough + next if $age < $maxage; + + # unseen messages + next if $query->{data_level} > 1; + + # active window + next if $query->is_active && + $query->window->{refnum} == $win->{refnum}; + + # graceperiod + next if time - $query->{last_unread_msg} < $minage; + + # kill it off + Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'query_closed', + $query->{name}, $query->{server_tag}) + if Irssi::settings_get_bool('query_noisy'); + $query->destroy; + + } +} + +sub cmd_query { + my($data,$server,$witem) = @_; + my(@data) = split " ", $data; + + my(@params,@opts,$query,$tag,$nick); + my($state,$info,$save); + + while (@data) { + my $param = shift @data; + + if ($param =~ s/^-//) { + my $opt = $query_opts->{lc $param}; + + if ($opt) { + + if ($opt eq 'window') { + push @opts, "-$param"; + + } elsif ($opt eq 'immortal') { + $state->{immortal} = 1; + + } elsif ($opt eq 'info') { + $info = 1; + + } elsif ($opt eq 'mortal') { + $state->{immortal} = 0; + + } elsif ($opt eq 'timeout') { + $state->{maxage} = str2sec shift @data; + + } elsif ($opt eq 'save') { + $save++; + + } else { + # unhandled known opt + + } + + } elsif ($tag = Irssi::server_find_tag($param)) { + $tag = $tag->{tag}; + push @opts, "-$tag"; + + } else { + # bogus opt... + push @opts, "-$param"; + + } + + } else { + # normal parameter + push @params, $param; + + } + } + + if (@params) { + Irssi::signal_continue("@opts @params",$server,$witem); + + # find the query... + my $serv = Irssi::server_find_tag($tag || $server->{tag}); + return unless $serv; + $query = $serv->window_item_find($params[0]); + + } else { + + if ($witem && $witem->{type} eq 'QUERY') { + $query = $witem; + } + + } + + if ($query) { + $nick = $query->{name}; + $tag = lc $query->{server_tag}; + + my $opts; + for (keys %$state) { + $state{$tag}{$nick}{$_} = $state->{$_}; + $opts++; + } + + $state = $state{$tag}{$nick}; + + if ($info) { + Irssi::signal_stop(); + my(@items,$key,$val); + + my $timeout = Irssi::settings_get_time('query_autoclose')/1000; + $timeout = $state->{maxage} if defined $state->{maxage}; + + if ($timeout) { + $timeout .= " (".sec2str($timeout).")"; + } else { + $timeout .= " (Off)"; + } + + @items = ( + Server => $query->{server_tag}, + Nick => $nick, + Address => $state->{address}, + Created => time2str($query->{createtime}), + Immortal => $state->{immortal}?'Yes':'No', + Timeout => $timeout, + Idle => sec2str(time - $state->{time}), + ); + + $query->printformat(MSGLEVEL_CLIENTCRAP, 'query_info_header'); + while (($key,$val) = splice @items, 0, 2) { + $query->printformat(MSGLEVEL_CLIENTCRAP, 'query_info', + $key, $val); + } + $query->printformat(MSGLEVEL_CLIENTCRAP, 'query_info_footer'); + + return; + } + + if ($save) { + Irssi::signal_stop; + + unless ($state->{address}) { + $query->printformat(MSGLEVEL_CLIENTCRAP, + 'query_crap', 'This query has no address yet'); + return; + } + + my $mask = Irssi::Irc::get_mask($nick, $state->{address}, + Irssi::Irc::MASK_USER | + Irssi::Irc::MASK_DOMAIN + ); + + for (qw(immortal maxage)) { + if (exists $state->{$_}) { + $defaults{$mask}{$_} = $state->{$_}; + } else { + delete $defaults{$mask}{$_}; + } + } + + save_defaults; + + return; + } + + if (!@params) { + Irssi::signal_stop; + return if $opts; + + if ($state{$tag}{$nick}{immortal}) { + $witem->printformat(MSGLEVEL_CLIENTCRAP, + 'query_crap', 'This query is immortal'); + } else { + $witem->command("unquery") + if Irssi::settings_get_bool('query_unqueries'); + } + + } + + } + +} + +sub cmd_unquery { + my($data,$server,$witem) = @_; + my($param) = split " ", $data; + my($query,$tag,$nick); + + if ($param) { + $query = $server->query_find($param) if $server; + } else { + $query = $witem if $witem && $witem->{type} eq 'QUERY'; + } + + if ($query) { + $nick = $query->{name}; + $tag = lc $query->{server_tag}; + + if ($state{$tag}{$nick}{immortal}) { + if ($param) { + $witem->printformat(MSGLEVEL_CLIENTCRAP, + 'query_crap', + "Query with $nick is immortal"); + } else { + $witem->printformat(MSGLEVEL_CLIENTCRAP, + 'query_crap', + 'This query is immortal'); + } + Irssi::signal_stop; + } + } +} + +Irssi::command_bind('query', 'cmd_query'); +Irssi::command_bind('unquery', 'cmd_unquery'); +Irssi::command_set_options('query', 'immortal mortal info save +timeout'); +abbrev $query_opts, qw(window immortal mortal info save timeout); + +#Irssi::command_bind('debug', sub { print Dumper \%state }); +#Irssi::command_bind('query_save', 'sig_session_save'); +#Irssi::command_bind('query_restore', 'sig_session_restore'); + +Irssi::theme_register( +[ + 'query_created', + '{line_start}{hilight Query:} started with {nick $0} [$1] in window $2', + + 'query_closed', + '{line_start}{hilight Query:} closed with {nick $0} [$1]', + + 'query_info_header', '', + + 'query_info_footer', '', + + 'query_crap', + '{line_start}{hilight Query:} $0', + + 'query_warn', + '{line_start}{hilight Query:} {error Warning:} $0', + + 'query_info', + '%#$[8]0: $1', + +]); + +Irssi::settings_add_bool('query', 'query_autojump_own', 1); +Irssi::settings_add_bool('query', 'query_autojump', 0); +Irssi::settings_add_bool('query', 'query_noisy', 1); +Irssi::settings_add_bool('query', 'query_unqueries', + Irssi::version < 20020919.1507 || + Irssi::version >= 20021006.1620 ); + +Irssi::settings_add_time('query', 'query_autoclose', 0); +Irssi::settings_add_time('query', 'query_autoclose_grace', '5min'); + +Irssi::signal_add_last('message own_private', 'sig_message_own_private'); +Irssi::signal_add_last('message private', 'sig_message_private'); + +Irssi::signal_add_last('query created', 'sig_query_created'); + +Irssi::signal_add('print text', 'sig_print_message'); + +Irssi::signal_add('query address changed', 'sig_query_address_changed'); +Irssi::signal_add('query destroyed', 'sig_query_destroyed'); +Irssi::signal_add('query nick changed', 'sig_query_nick_changed'); + +Irssi::signal_add('redir query userhost', 'sig_redir_query_userhost'); + +Irssi::signal_add('session save', 'sig_session_save'); +Irssi::signal_add('session restore', 'sig_session_restore'); + +Irssi::timeout_add(5000, 'check_queries', undef); + +load_defaults; + +for my $query (Irssi::queries) { + my($tag) = lc $query->{server_tag}; + my($nick) = $query->{name}; + + $state{$tag}{$nick}{time} + ||= $query->{last_unread_msg} || $query->{createtime} || time; + + set_defaults($query->{server}, $nick, $query->{address}); +} + +if (Irssi::settings_get_time("autoclose_query")) { + Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'query_warn', + "autoclose_query is set, please set to 0"); +} |