# ho_lusercount.pl # # $Id: ho_lusercount.pl,v 1.3 2004/08/15 08:53:05 jvunder REL_0_3 $ # # Part of the Hybrid Oper Script Collection. # # This provides a statusbar item containing the users on the server. # It uses /lusers for that, plus usermode +c to increase/decrease the # number of users whenever a client (dis)connects. # Known bugs: # * Doing /lusers may confuse the script. # * You need to do a manual /lusers for every network after loading. # * The "I have clients" server notice gets eaten. It is resent, # but not with optimal formatting. # Todo: # Add different formats for increase/decrease history lines with different # colours. # /lusers output # >> :irc.efnet.nl 255 Garion :I have 10970 clients and 2 servers use strict; use vars qw($VERSION %IRSSI $SCRIPT_NAME); # Why doesn't use constant work in my irssi? *confused* #use constant MIN_HISTORY_DELTA_TIME => 10; # seconds my $MIN_HISTORY_DELTA_TIME = 10; use Irssi qw( settings_get_int settings_get_str settings_get_bool settings_set_int settings_set_str settings_set_bool settings_add_int settings_add_str settings_add_bool ); use Irssi::TextUI; # for statusbar use HOSC::again; use HOSC::again 'HOSC::Base'; use HOSC::again 'HOSC::Tools'; # --------------------------------------------------------------------- ($VERSION) = '$Revision: 1.3 $' =~ / (\d+\.\d+) /; %IRSSI = ( authors => 'Garion', contact => 'garion@efnet.nl', name => 'ho_lusercount', description => 'Statusbar item with number of clients, and client history graph.', license => 'Public Domain', url => 'http://www.garion.org/irssi/hosc.php', changed => '24 April 2004 22:24:08', ); $SCRIPT_NAME = 'Lusercount'; # --------------------------------------------------------------------- # # Thanks to: # # - mofo, for suggestions # --------------------------------------------------------------------- # Hash that holds a {$network}->{variable} substructure. my %luserinfo; # Statusbar timer refresh handle my $delta_handle_statusbar; # Statusbar timer refresh time my $delta_statusbar; # History timer refresh handle my $delta_handle_history; # History timer refresh time my $delta_history; # --------------------------------------------------------------------- # >> :irc.efnet.nl 255 Garion :I have 10970 clients and 2 servers sub event_lusers_output { my ($server, $msg) = @_; if ($msg =~ /I have ([0-9]+) clients/o) { for my $n (split /\s+/, lc(settings_get_str('ho_lusercount_networks')) ) { if (lc($n) eq lc($server->{tag})) { $luserinfo{$n}->{numclients} = $1; } } # Re-send the message. Needs proper formatting..? $msg =~ s/^[^:]+://; Irssi::print($msg, MSGLEVEL_CRAP); Irssi::statusbar_items_redraw('lusercount'); } } # --------------------------------------------------------------------- # A Server Event has occurred. Check if it is a server NOTICE; # if so, process it. sub event_serverevent { my ($server, $msg, $nick, $hostmask) = @_; my ($nickname, $username, $hostname); # If it is not a NOTICE, we don't want to have anything to do with it. return if $msg !~ /^NOTICE/o; # If the hostmask is set, it is not a server NOTICE, so we'll ignore it # as well. return if length($hostmask) > 0; # Check whether the server notice is on one of the networks we are # monitoring. my $watch_this_network = 0; foreach my $network (split /\s+/, lc(settings_get_str('ho_lusercount_networks')) ) { if ($network eq lc($server->{tag})) { $watch_this_network = 1; last; } } return unless $watch_this_network; my $ownnick = $server->{'nick'}; # Remove the NOTICE part from the message # NOTE: this is probably unnecessary. $msg =~ s/^NOTICE $ownnick ://; # Remove the server prefix # NOTE: this is probably unnecessary. #$msg =~ s/^$prefix//; process_event($server, $msg); } # --------------------------------------------------------------------- # This function takes a server notice and matches it with a few regular # expressions to see if any special action needs to be taken. sub process_event { my ($server, $msg) = @_; # Client connect: nick, user, host, ip, class, realname if (index($msg ,"tice -- Client connecting: ") >= 0) { client_add(lc($server->{tag})); if (settings_get_bool('ho_lusercount_suppress_snotices')) { Irssi::signal_stop(); } return; } # Client exit: nick, user, host, reason, ip if (index($msg, "tice -- Client exiting: ") >= 0) { client_remove(lc($server->{tag})); if (settings_get_bool('ho_lusercount_suppress_snotices')) { Irssi::signal_stop(); } return; } } # --------------------------------------------------------------------- # Refreshes the number of clients connected in the past N seconds, # where N is the setting ho_lusercount_delta_statusbar. sub delta_statusbar { foreach my $n (split /\s+/, lc(settings_get_str('ho_lusercount_networks')) ) { my $l = $luserinfo{$n}; $l->{deltaclients} = $l->{deltaclients_temp}; $l->{deltaclients_temp} = 0; $l->{connectedclients} = $l->{connectedclients_temp}; $l->{connectedclients_temp} = 0; $l->{disconnectedclients} = $l->{disconnectedclients_temp}; $l->{disconnectedclients_temp} = 0; } my $time = settings_get_int("ho_lusercount_delta_statusbar") * 1000; $time = 5000 if $time < 1000; if ($time != $delta_statusbar) { Irssi::print("Changing statusbar timer."); Irssi::timeout_remove($delta_handle_statusbar) if $delta_handle_statusbar; $delta_statusbar = $time; Irssi::timeout_add($delta_statusbar, 'delta_statusbar', undef); } } # --------------------------------------------------------------------- sub delta_history { my $now = time(); foreach my $n (split /\s+/, lc(settings_get_str('ho_lusercount_networks')) ) { $luserinfo{$n}->{client_history}->{$now} = $luserinfo{$n}->{numclients}; } clean_delta_history(); my $time = settings_get_int("ho_lusercount_delta_history") * 1000; $time = $MIN_HISTORY_DELTA_TIME if $time < $MIN_HISTORY_DELTA_TIME; if ($time != $delta_history) { ho_print("lusercount - Changing history timer."); if ($delta_handle_history) { Irssi::timeout_remove($delta_handle_history); } $delta_history = $time; Irssi::timeout_add($delta_history, 'delta_history', undef); } } # --------------------------------------------------------------------- sub clean_delta_history { my $keeptime = settings_get_int("ho_lusercount_history_time"); my $now = time(); for my $n (split /\s+/, lc(settings_get_str('ho_lusercount_networks')) ) { next unless defined $luserinfo{$n}; for my $time (sort {$a <=> $b} keys %{ $luserinfo{$n}->{client_history} }) { if ($now - $time > $keeptime) { delete($luserinfo{$n}->{client_history}->{$time}); } else { last; } } } } # --------------------------------------------------------------------- # Adds a client object to the client array, and an entry in the client # hashtable pointing to this object. sub client_add { my ($n) = @_; # Increase the number of clients. $luserinfo{$n}->{numclients}++; $luserinfo{$n}->{deltaclients_temp}++; $luserinfo{$n}->{connectedclients_temp}++; # Redraw the statusbar item. Irssi::statusbar_items_redraw("lusercount"); } # --------------------------------------------------------------------- # Removes a client from the client array and from the client hash. # Argument is the nickname of the client to be removed. # If this nickname is not in the client array, nothing happens. sub client_remove { my ($n) = @_; # Decrease the number of clients. $luserinfo{$n}->{numclients}--; $luserinfo{$n}->{deltaclients_temp}--; $luserinfo{$n}->{disconnectedclients_temp}++; # Redraw the statusbar item. Irssi::statusbar_items_redraw("lusercount"); } # --------------------------------------------------------------------- sub cmd_lusercount { my ($data, $server, $item) = @_; if ($data =~ /^[(history)|(help)]/i ) { Irssi::command_runsub ('lusercount', $data, $server, $item); } else { ho_print("Use '/lusercount history ' to show the ". "history of that network or '/lusercount help' for help.") } } # --------------------------------------------------------------------- sub cmd_lusercount_help { print_help(); } # --------------------------------------------------------------------- # Shows the history of connected clients. sub cmd_lusercount_history { my ($data, $server, $item) = @_; if ($data =~ /^\s*$/) { ho_print("Use /LUSERCOUNT HISTORY [time in minutes]."); return; } my ($tag, $time); if ($data =~ /^(\S+)\s+(\d+)/) { ($tag, $time) = ($1, int($2)); } else { $tag = lc($data); $time = 0; } if (!defined $luserinfo{$tag}) { ho_print("No lusercount history available for network '$tag'."); return; } # Determine the begin time of the history to display. my $begin_time = 0; $begin_time = int(time - 60 * $time) if $time > 0; Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'ho_lusercount_history_begin', $tag); my $previous_time = undef; my $diff = 0; my $max_diff = get_max_diff($tag, $begin_time); my $graph_width = settings_get_int('ho_lusercount_graph_width'); $graph_width++ unless $graph_width % 2; # make sure width is odd for my $time (sort {$a <=> $b} keys %{ $luserinfo{$tag}->{client_history} } ) { next if $time < $begin_time; my $humantime = sprintf("%02d:%02d", (localtime($time))[2], (localtime($time))[1]); my $diff_col = '%n'; if (defined $previous_time) { $diff = $luserinfo{$tag}->{client_history}->{$time} - $luserinfo{$tag}->{client_history}->{$previous_time}; if ($diff > 0.6 * $max_diff) { $diff_col = '%G'; } elsif ($diff < - 0.6 * $max_diff) { $diff_col = '%R'; } } Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'ho_lusercount_history_line', $humantime, sprintf("%4d", $luserinfo{$tag}->{client_history}->{$time}), sprintf("%3d", $diff), get_graph_line($diff, $max_diff, $graph_width) ); $previous_time = $time; } Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'ho_lusercount_history_end', $tag); } # --------------------------------------------------------------------- # Returns the maximum difference between 2 consecutive timestamps in # the client history of $network_tag. sub get_max_diff { my ($network_tag, $begin_time) = @_; my $previous_time = undef; my $diff = 0; my $maxdiff = 0; $begin_time = 0 unless defined $begin_time; for my $time ( sort {$a <=> $b} keys %{ $luserinfo{$network_tag}->{client_history} } ) { next if $time < $begin_time; if (defined $previous_time) { $diff = abs( $luserinfo{$network_tag}->{client_history}->{$time} - $luserinfo{$network_tag}->{client_history}->{$previous_time} ); } $maxdiff = $diff if $diff > $maxdiff; $previous_time = $time; } return $maxdiff; } # --------------------------------------------------------------------- sub get_graph_line { my ($diff, $max_diff, $width) = @_; my $half_width = int(0.5 * $width); my $left = " " x $half_width; my $right = $left; # Don't bother if there is no difference. return $left . '|' . $right if $max_diff == 0; my $num_blocks = int( abs( $half_width * ($diff / $max_diff) ) ); if ($diff > 0) { $right = '*' x $num_blocks . ' ' x ($half_width - $num_blocks); } elsif ($diff < 0) { $left = ' ' x ($half_width - $num_blocks) . '*' x $num_blocks; } return $left . '|' . $right; } # --------------------------------------------------------------------- # Statusbar item determination function. sub lusercount_sb { my ($item, $get_size_only) = @_; my $txt = "{sb "; for my $n (split /\s+/, lc(settings_get_str('ho_lusercount_networks'))) { if (defined($luserinfo{$n})) { my $info = settings_get_str('ho_lusercount_format'); $info =~ s/\$n/$n/; $info =~ s/\$c/$luserinfo{$n}->{numclients}/; $info =~ s/\$D/$luserinfo{$n}->{deltaclients}/; $info =~ s/\$i/$luserinfo{$n}->{connectedclients}/; $info =~ s/\$d/$luserinfo{$n}->{disconnectedclients}/; $txt .= $info . " "; } } $txt =~ s/ $/}/; $item->default_handler($get_size_only, "$txt", undef, 1); } # --------------------------------------------------------------------- ho_print_init_begin($SCRIPT_NAME); Irssi::signal_add_first('server event', 'event_serverevent'); Irssi::signal_add_first('event 255', 'event_lusers_output'); Irssi::command_bind('lusercount', 'cmd_lusercount'); Irssi::command_bind('lusercount help', 'cmd_lusercount_help'); Irssi::command_bind('lusercount history', 'cmd_lusercount_history'); settings_add_int('ho', 'ho_lusercount_delta_statusbar', 10); settings_add_int('ho', 'ho_lusercount_delta_history', 300); settings_add_int('ho', 'ho_lusercount_history_time', 86400); settings_add_str('ho', 'ho_lusercount_networks', ""); settings_add_bool('ho', 'ho_lusercount_suppress_snotices', 0); settings_add_str('ho', 'ho_lusercount_format', '$n: $c/$D ($i:$d)'); settings_add_int('ho', 'ho_lusercount_graph_width', 40); Irssi::theme_register( [ 'ho_lusercount_history_begin', '%Y+-%n Lusercount history for $0:', 'ho_lusercount_history_line', '%Y|%n $0 - $1 ($2) $3', 'ho_lusercount_history_end', '%Y+-%n End of lusercount history for $0.', ]); Irssi::statusbar_item_register('lusercount', '{sb $1-}', 'lusercount_sb'); Irssi::statusbar_items_redraw("lusercount"); # Add the statusbar update timer $delta_statusbar = settings_get_int("ho_lusercount_delta_statusbar") * 1000; $delta_statusbar = 5000 if $delta_statusbar < 1000; $delta_handle_statusbar = Irssi::timeout_add($delta_statusbar, 'delta_statusbar', undef); # Add the history update timer $delta_history = settings_get_int("ho_lusercount_delta_history") * 1000; $delta_history = $MIN_HISTORY_DELTA_TIME if $delta_history < $MIN_HISTORY_DELTA_TIME; $delta_handle_history = Irssi::timeout_add($delta_history, 'delta_history', undef); if (length(settings_get_str('ho_lusercount_networks')) == 0) { ho_print("Use /SET ho_lusercount_networks to set ". "the list of network tags the lusercount must be tracked on. This is ". "a space separated list."); } ho_print_init_end($SCRIPT_NAME); ho_print('Use /LUSERCOUNT HELP for help.'); # --------------------------------------------------------------------- sub print_help { ho_print_help('head', $SCRIPT_NAME); ho_print_help('section', 'Syntax'); ho_print_help('syntax', 'LUSERCOUNT HISTORY [