use Irssi 20020101.0001 (); use strict; use warnings; use Irssi::TextUI; use vars qw($VERSION %IRSSI); $VERSION = "0.6.0"; %IRSSI = ( authors => 'BC-bd', contact => 'bd@bc-bd.org', name => 'chanact', description => 'Adds new powerful and customizable [Act: ...] item (chanelnames,modes,alias). Lets you give alias characters to windows so that you can select those with meta-', license => 'GNU GPLv2 or later', url => 'http://bc-bd.org/blog/irssi/' ); # Please send patches / pull requests to the email listed unter contact above # and not to the irssi/scripts.irssi.org repository on github. # Adds new powerful and customizable [Act: ...] item (chanelnames,modes,alias). # Lets you give alias characters to windows so that you can select those with # meta-. # # for irssi 0.8.2 by bd@bc-bd.org # # inspired by chanlist.pl by 'cumol@hammerhart.de' # ######### # Contributors ######### # # veli@piipiip.net original /window_alias code # qrczak@knm.org.pl chanact_abbreviate_names # qerub@home.se Extra chanact_show_mode and chanact_chop_status # madduck@madduck.net Better channel aliasing (case-sensitive, cross-network) # chanact_filter_windowlist basis # jast@heapsort.de Updated documentation # ivo@o2w.nl win->{hilight} patch # Bazerka base patch for sorting by level change # updated documentation # mrtnpaolo@gmail.com rename commands # tslocum@gmail.com Case-insensitive aliases # ######### # USAGE ### # # copy the script to ~/.irssi/scripts/ # # In irssi: # # /script load chanact # /statusbar window add -after act chanact # # If you want the item to appear on another position read the help # for /statusbar. # To remove the [Act: 1,2,3] item type: # # /statusbar window remove act # # To see all chanact options type: # # / set chanact_ # # After these steps you have your new statusbar item and you can start giving # aliases to your windows. Go to the window you want to give the alias to # and say: # # /chanact_window_alias # # You can remove the aliases with from an aliased window: # # /chanact_window_unalias # # To see a list of your windows use: # # /window list # # To make your bindings permanent you will need to save the config and layout # before quiting irssi: # # /save # /layout save # ######### # OPTIONS ######### # # /set chanact_chop_status # * ON : shorten (Status) to S # * OFF : don't do it # # /set chanact_sort # sort by ... # refnum : refnum # activity : last active window # level+refnum : data_level of window, then refnum # level+activity : data_level then activity # # /set chanact_display # * string : Format String for one Channel. The following $'s are expanded: # $C : Channel # $N : Number of the Window # $M : Mode in that channel # $H : Start highlightning # $S : Stop highlightning # * example: # # /set chanact_display $H$N:$M.$S$C # # will give you on #irssi.de if you have voice # # [3:+.#irssi.de] # # with '3:+.' highlighted and the channel name printed in regular color # # /set chanact_case_sensitive # * ON : Aliases are case-sensitive # * OFF : Aliases are case-insensitive # # Existing aliases must be reapplied after changing this option # # Switching from OFF to ON _after_ aliases have been defined, and # then redefining or changing an existing alias will leave some # bindings behind, e.g. # # /set chanact_case_sensitive OFF # /chanact_window_alias x # # -> window reachable with meta-x/meta-X # # /set chanact_case_sensitive ON # /chanact_window_alias y # # -> window reachable with meta-y/meta-X # # /set chanact_display_alias # as 'chanact_display' but is used if the window has an alias and # 'chanact_show_alias' is set to on. # # /set chanact_show_names # * ON : show the channelnames after the number/alias # * OFF : don't show the names # # /set chanact_abbreviate_names # * 0 : don't abbreviate # * : strip channel name prefix character and leave only # that many characters of the proper name # # /set chanact_show_alias # * ON : show the aliase instead of the refnum # * OFF : shot the refnum # # /set chanact_header # * : Characters to be displayed at the start of the item. # Defaults to: "Act: " # # /set chanact_separator # * : Charater to use between the channel entries # # /set chanact_autorenumber # * ON : Move the window automatically to first available slot # starting from "chanact_renumber_start" when assigning # an alias to window. Also moves the window back to a # first available slot from refnum 1 when the window # loses it's alias. # * OFF : Don't move the windows automatically # # /set chanact_renumber_start # * : Move the window to first available slot after this # num when "chanact_autorenumber" is ON. # # /set chanact_remove_hash # * ON : Remove &#+!= from channel names # * OFF : Don't touch channel names # # /set chanact_remove_prefix # * : Regular expression used to remove from the # beginning of the channel name. # * example : # To shorten a lot of debian channels: # # /set chanact_remove_prefix deb(ian.(devel-)?)? # # /set chanact_filter # * 0 : show all channels # * 1 : hide channels without activity # * 2 : hide channels with only join/part/etc messages # * 3 : hide channels with text messages # * 4 : hide all channels (now why would you want to do that) # # /set chanact_filter_windowlist # * : space-separated list of windows for which to use # chanact_filter_windowlist_level instead of # chanact_filter. # # Alternatively, an entry can be postfixed with # a comma (',') and the level to use for that # window. # # The special string @QUERIES matches all queries. # # /set chanact_filter_windowlist_level # Use this level to filter all windows listed in chanact_filter_windowlist. # You can use these two settings to apply different filter levels to different # windows. Defaults to 0. # ######### # HINTS ######### # # If you have trouble with wrong colored entries your 'default.theme' might # be too old. Try on a shell: # # $ mv ~/.irssi/default.theme /tmp/ # # And in irssi: # /reload # /save # ### ################# my %show = ( 0 => "{%n ", # NOTHING 1 => "{sb_act_text ", # TEXT 2 => "{sb_act_msg ", # MSG 3 => "{sb_act_hilight ", # HILIGHT ); # comparison operators for our sort methods my %sort = ( 'refnum' => '$a->{refnum} <=> $b->{refnum};', 'activity' => '$b->{last_line} <=> $a->{last_line};', 'level+refnum' => '$b->{data_level} <=> $a->{data_level} || $a->{refnum} <=> $b->{refnum};', 'level+activity'=> '$b->{data_level} <=> $a->{data_level} || $b->{last_line} <=> $a->{last_line};', ); my ($actString,$needRemake); sub expand { my ($string, %format) = @_; my ($exp, $repl); $string =~ s/\$$exp/$repl/g while (($exp, $repl) = each(%format)); return $string; } # method will get called every time the statusbar item will be displayed # but we dont need to recreate the item every time so we first # check if something has changed and only then we recreate the string # this might just save some cycles sub chanact { my ($item, $get_size_only) = @_; if ($needRemake) { remake(); } $item->default_handler($get_size_only, $actString, "", 1); } # build a hash to easily access special levels based on # chanact_filter_windowlist sub calculate_windowlist() { my @matchlist = split ' ', Irssi::settings_get_str('chanact_filter_windowlist'); my $default = Irssi::settings_get_int('chanact_filter_windowlist_level'); my %windowlist; foreach my $m (@matchlist) { my ($name, $level) = split(/,/, $m); $windowlist{$name} = $level ? $level : $default; } return %windowlist; } # calculate level per window sub calculate_levels(@) { my (@windows) = @_; my %matches = calculate_windowlist(); my $default = Irssi::settings_get_int('chanact_filter'); my %levels; foreach my $win (@windows) { !ref($win) && next; my $name = $win->get_active_name; # skip nameless windows next unless $name; if ($name && exists($matches{$name})) { $levels{$name} = $matches{$name}; } else { $levels{$name} = $default; } } if (exists($matches{'@QUERIES'})) { $levels{'@QUERIES'} = $matches{'@QUERIES'}; } else { $levels{'@QUERIES'} = $default; } return %levels; } # this is the real creation method sub remake() { my ($afternumber,$finish,$hilight,$mode,$number,$display,@windows); my $separator = Irssi::settings_get_str('chanact_separator'); my $abbrev = Irssi::settings_get_int('chanact_abbreviate_names'); my $remove_prefix = Irssi::settings_get_str('chanact_remove_prefix'); my $remove_hash = Irssi::settings_get_bool('chanact_remove_hash'); my $method = $sort{Irssi::settings_get_str('chanact_sort')}; @windows = sort { eval $method } Irssi::windows(); my %levels = calculate_levels(@windows); $actString = ""; foreach my $win (@windows) { # since irssi is single threaded this shouldn't happen !ref($win) && next; my $active = $win->{active}; # define $type to emtpy string and overwrite if we do have an # active item. we need this to display windows without active # items e.g. '(status)' my $type = ""; $type = $active->{type} if $active; my $name = $win->get_active_name; # skip windows without a name next unless $name; my $filter_level = $type eq 'QUERY' ? $levels{'@QUERIES'} : $levels{$name}; # now, skip windows with data of level lower than the # filter level next if ($win->{data_level} < $filter_level); # alright, the activity is important, let's show the window # after a bit of additional processing. # (status) is an awfull long name, so make it short to 'S' # some people don't like it, so make it configurable if (Irssi::settings_get_bool('chanact_chop_status') && $name eq "(status)") { $name = "S"; } # check if we should show the mode $mode = ""; if ($type eq "CHANNEL") { my $server = $win->{active_server}; !ref($server) && next; my $channel = $server->channel_find($name); !ref($channel) && next; my $nick = $channel->nick_find($server->{nick}); !ref($nick) && next; if ($nick->{op}) { $mode = "@"; } elsif ($nick->{voice}) { $mode = "+"; } elsif ($nick->{halfop}) { $mode = "%"; } } # in case we have a specific hilightcolor use it if ($win->{hilight_color}) { $hilight = "{sb_act_hilight_color $win->{hilight_color} "; } else { $hilight = $show{$win->{data_level}}; } if ($remove_prefix) { $name =~ s/^([&#+!=]?)$remove_prefix/$1/; } if ($abbrev) { if ($name =~ /^[&#+!=]/) { $name = substr($name, 1, $abbrev + 1); } else { $name = substr($name, 0, $abbrev); } } if ($remove_hash) { $name =~ s/^[&#+!=]//; } if (Irssi::settings_get_bool('chanact_show_alias') == 1 && $win->{name} =~ /^([a-zA-Z+]):(.+)$/) { $number = "$1"; $display = Irssi::settings_get_str('chanact_display_alias'); } else { $number = $win->{refnum}; $display = Irssi::settings_get_str('chanact_display'); } # fixup { and } in nicks, those are used by irssi themes $name =~ s/([{}])/%$1/g; $actString .= expand($display,"C",$name,"N",$number,"M",$mode,"H",$hilight,"S","}{sb_background}").$separator; } # assemble the final string if ($actString ne "") { # Remove the last separator $actString =~ s/$separator$//; $actString = "{sb ".Irssi::settings_get_str('chanact_header').$actString."}"; } # no remake needed any longer $needRemake = 0; } # method called because of some events. here we dont remake the item but just # remember that we have to remake it the next time we are called sub chanactHasChanged() { # if needRemake is already set, no need to trigger a redraw as we will # be redrawing the item anyway. return if $needRemake; $needRemake = 1; Irssi::statusbar_items_redraw('chanact'); } sub setup_changed { my $method = Irssi::settings_get_str('chanact_sort'); unless (exists($sort{$method})) { Irssi::print("chanact: invalid sort method, setting to 'refnum'." ." valid methods are: ".join(", ", sort(keys(%sort)))); my $method = Irssi::settings_set_str('chanact_sort', 'refnum'); } chanactHasChanged(); } # Remove key binding for current window sub unbind { my ($name, $server) = @_; # chanact'ified windows have a name like this: X:servertag/name. if we # can't find anything like this we return and do not unbind nor renumber # anything my ($key, $tag) = split(/:/, $name); return unless $tag; ($tag, $name) = split('/', $tag); return unless (length($key) == 1); if (Irssi::settings_get_bool('chanact_case_sensitive')) { $server->command("/bind -delete meta-$key"); } else { $server->command("/bind -delete meta-" . lc($key)); $server->command("/bind -delete meta-" . uc($key)); } } # Remove alias sub cmd_window_unalias { my ($data, $server, $witem) = @_; if ($data ne '') { Irssi::print("chanact: /chanact_window_unalias does not take any ". "parameters, Run it in the window you want to unalias"); return; } my $win = Irssi::active_win(); my $name = Irssi::active_win()->{name}; unbind($name, $server); # set the windowname back to it's old one. We don't bother checking # for a vaild name here, as we want to remove the current one and if # worse comes to wors set an empty one. $win->set_name($name); # if autorenumbering is off, we are done. return unless (Irssi::settings_get_bool('chanact_autorenumber')); # we are renumbering, so move the window to the lowest available # refnum. my $refnum = 1; while (Irssi::window_find_refnum($refnum)) { $refnum++; } $win->set_refnum($refnum); Irssi::print("chanact: moved wintow to refnum $refnum"); } # Make an alias sub cmd_window_alias { my ($data, $server, $witem) = @_; my $rn_start = Irssi::settings_get_int('chanact_renumber_start'); unless ($data =~ /^[a-zA-Z+]$/) { Irssi::print("Usage: /chanact_window_alias "); return; } # in case of an itemless window $witem is undef, thus future operations # on it fail. to prevent this we pull in the current window. # # Also we need to initialize $winname, else we would get a broken name: # # 'name' => 'S:IRCnet/S:IRCnet/', # my $window; my $winname = ""; if (defined($witem)) { $window = $witem->window(); $winname = $witem->{name}; } else { $window = Irssi::active_win(); $winname = $window->{name}; } unbind($window->{name}, $server); my $winnum = $window->{refnum}; if (Irssi::settings_get_bool('chanact_autorenumber') == 1 && $window->{refnum} < $rn_start) { my $old_refnum = $window->{refnum}; $winnum = $rn_start; # Find the first available slot and move the window while (Irssi::window_find_refnum($winnum)) { $winnum++; } $window->set_refnum($winnum); Irssi::print("Moved the window from $old_refnum to $winnum"); } my $winserver = $window->{active_server}->{tag}; my $winhandle = "$winserver/$winname"; # cmd_window_unalias relies on a certain format here my $name = "$data:$winhandle"; $window->set_name($name); if (Irssi::settings_get_bool('chanact_case_sensitive')) { $server->command("/bind meta-$data change_window $name"); } else { $server->command("/bind meta-" . lc($data) . " change_window $name"); $server->command("/bind meta-" . uc($data) . " change_window $name"); } Irssi::print("Window $winhandle is now accessible with meta-$data"); } $needRemake = 1; # Window alias command Irssi::command_bind('chanact_window_alias','cmd_window_alias'); Irssi::command_bind('chanact_window_unalias','cmd_window_unalias'); # our config item Irssi::settings_add_str('chanact', 'chanact_display', '$H$N:$M$C$S'); Irssi::settings_add_str('chanact', 'chanact_display_alias', '$H$N$M$S'); Irssi::settings_add_int('chanact', 'chanact_abbreviate_names', 0); Irssi::settings_add_bool('chanact', 'chanact_case_sensitive', 1); Irssi::settings_add_bool('chanact', 'chanact_show_alias', 1); Irssi::settings_add_str('chanact', 'chanact_separator', " "); Irssi::settings_add_bool('chanact', 'chanact_autorenumber', 0); Irssi::settings_add_bool('chanact', 'chanact_remove_hash', 0); Irssi::settings_add_str('chanact', 'chanact_remove_prefix', ""); Irssi::settings_add_int('chanact', 'chanact_renumber_start', 50); Irssi::settings_add_str('chanact', 'chanact_header', "Act: "); Irssi::settings_add_bool('chanact', 'chanact_chop_status', 1); Irssi::settings_add_str('chanact', 'chanact_sort', 'refnum'); Irssi::settings_add_int('chanact', 'chanact_filter', 0); Irssi::settings_add_str('chanact', 'chanact_filter_windowlist', ""); Irssi::settings_add_int('chanact', 'chanact_filter_windowlist_level', 0); # register the statusbar item Irssi::statusbar_item_register('chanact', '$0', 'chanact'); # according to cras we shall not call this # Irssi::statusbars_recreate_items(); # register all that nifty callbacks on special events Irssi::signal_add_last('setup changed', 'setup_changed'); Irssi::signal_add_last('window changed', 'chanactHasChanged'); Irssi::signal_add_last('window item changed', 'chanactHasChanged'); Irssi::signal_add_last('window hilight', 'chanactHasChanged'); Irssi::signal_add_last('window item hilight', 'chanactHasChanged'); Irssi::signal_add("window created", "chanactHasChanged"); Irssi::signal_add("window destroyed", "chanactHasChanged"); Irssi::signal_add("window name changed", "chanactHasChanged"); Irssi::signal_add("window activity", "chanactHasChanged"); Irssi::signal_add("print text", "chanactHasChanged"); Irssi::signal_add('nick mode changed', 'chanactHasChanged'); ############### ### # # Changelog # # 0.6.0 # - fixed URL # - now with 'use warnings' # - fix cmd_window_unalias call from cmd_window_alias # - fix Use of uninitialized value $name in hash element warnings # - return from cmd_window_unalias if the window has no valid # chanact'ified name # - rename /window_(un)alias to /chanact_window_(un)alias # - fix refnum renumber race # - added setting to allow case-insensitive window aliases # # 0.5.14 # - fix itemless window handling, thx Bazerka # - fix /window_alias for itemless windows # - fix /window_unalias. Also longer takes an argument # - added sorting by level, based on patch by Bazerka # + retired chanact_sort_by_activity, integrated in chanact_sort # # 0.5.13 # - trivial cleanup in cmd_window_alias() # - updated documentation regarding /layout save, thx Bazerka # - removed cmd_rebuild_aliases(), no longer working since we use channel # names to select windows and not refnums # - removed refnum_changed(), see cmd_rebuild_aliases() above # # 0.5.12 # - Use comma instead of colon as windowlist separator, patch by martin f. # krafft, reported by James Vega # # 0.5.11 # - added chanact_filter_windowlist based on a patch by madduck@madduck.net # - fixed display error for nicks/channels with { or } in them # - fixed chanact_header, was hidden behind chanact_filter # - fixed documentation # + removed chanact_show_mode, long gone # # 0.5.10 # - fixed irssi crash when using Irssi::print from within remake() # - added option to filter out some data levels, based on a patch by # Juergen Jung , see # https://bc-bd.org/trac/irssi/ticket/15 # + retired chanact_show_all in favour of chanact_filter # # 0.5.9 # - changes by stefan voelkel # + sort channels by activity, see # https://bc-bd.org/trac/irssi/ticket/5, based on a patch by jan # krueger # + fixed chrash on /exec -interactive, see # https://bc-bd.org/trac/irssi/ticket/7 # # - changes by Jan 'jast' Krueger , 2004-06-22 # + updated documentation in script's comments # # - changes by Ivo Timmermans # + honor actcolor /hilight setting if present # # 0.5.8 # - made aliases case-sensitive and include network in channel names by madduck # # 0.5.7 # - integrated remove patch by Christoph Berg # # 0.5.6 # - fixed a bug (#1) reported by Wouter Coekaert # # 0.5.5 # - some speedups from David Leadbeater # # # 0.5.4 # - added help for chanact_display_alias # # 0.5.3 # - added '+' to the available chars of aliase's # - added chanact_display_alias to allow different display modes if the window # has an alias # # 0.5.2 # - removed unused chanact_show_name settings (thx to Qerub) # - fixed $mode display # - guarded reference operations to (hopefully) fix errors on server disconnect # # 0.5.1 # - small typo fixed # # 0.5.0 # - changed chanact_show_mode to chanact_display. reversed changes from # Qerub through that, but kept funcionality. # - removed chanact_color_all since it is no longer needed # # 0.4.3 # - changes by Qerub # + added chanact_show_mode to show the mode just before the channel name # + added chanact_chop_status to be able to control the (status) chopping # [bd] minor implementation changes # - moved Changelog to the end of the file since it is getting pretty big # # 0.4.2 # - changed back to old version numbering sheme # - added '=' to Qrczak's chanact_abbreviate_names stuff :) # - added chanact_header # # 0.41q # - changes by Qrczak # + added setting 'chanact_abbreviate_names' # + windows are sorted by refnum; I didn't understand the old # logic and it broke sorting for numbers above 9 # # 0.41 # - minor updates # + fixed channel sort [veli] # + removed few typos and added some documentation [veli] # # 0.4 # - merge with window_alias.pl # + added /window_alias from window_alias.pl by veli@piipiip.net # + added setting 'chanact_show_alias' # + added setting 'chanact_show_names' # + changed setting 'chanact_show_mode' to int # + added setting 'chanact_separator' [veli] # + added setting 'chanact_autorenumber' [veli] # + added setting 'chanact_renumber_start' [veli] # + added /window_unalias [veli] # + moved setting to their own group 'chanact' [veli] # # 0.3 # - merge with chanlist.pl # + added setting 'chanact_show_mode' # + added setting 'chanact_show_all' # # 0.2 # - added 'Act' to the item # - added setting 'chanact_color_all' # - finally found format for statusbar hilight # # 0.1 # - Initial Release # ### ################