diff options
Diffstat (limited to 'scripts/synccheck.pl')
-rw-r--r-- | scripts/synccheck.pl | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/scripts/synccheck.pl b/scripts/synccheck.pl new file mode 100644 index 0000000..0bce93c --- /dev/null +++ b/scripts/synccheck.pl @@ -0,0 +1,346 @@ +# +# usage: /sync-check [channel (servers)|-stop] +# examples: +# /sync-check *.de +# /sync-check +# /sync-check #irssi +# /sync-check poznan.irc.pl +# /sync-check #irssi poznan.irc.pl *.de +# /sync-check -stop +# usage: /SET synccheck_show_all_errors [On/Off] +# + +use strict; +use Irssi 20020313 (); + +use vars qw($VERSION %IRSSI); +$VERSION = "0.4.9.1"; +%IRSSI = ( + authors => 'Marcin Rozycki', + contact => 'derwan@irssi.pl', + name => 'sync-check', + description => 'Script checking channel synchronization. Usage: /sync-check [channel (servers)|-stop]', + license => 'GNU GPL v2', + url => 'http://derwan.irssi.pl', + changed => 'Fri Aug 9 23:00:00 CEST 2002' +); + +my $synccheck = undef; + +sub _print ($$$) +{ + my ($server, $level, $msg) = @_; + if (defined $synccheck and $server and my $win = $server->channel_find($synccheck->{name})) { + $win->print($msg, $level); + } +} + +sub _endof +{ + %$synccheck = (), undef $synccheck if (defined $synccheck); + Irssi::print(shift) if (@_); +} + +sub _new ($) +{ + _endof; my $server = shift; + return 0 unless ($server and $server->{type} eq 'SERVER' and $server->{connected}); + + $synccheck = {}; + $synccheck->{time} = time; + $synccheck->{server} = $server->{address}; + $synccheck->{tag} = $server->{tag}; + $synccheck->{_error} = 0; + $synccheck->{_tested} = 0; + $synccheck->{_info} = 0; + + return $synccheck; +} + +sub _setchan ($) +{ + if (defined $synccheck) { + $synccheck->{name} = shift; + $synccheck->{channel} = lc($synccheck->{name}); + } +} + +sub _addlink ($) +{ + my $link = shift; + if (defined $synccheck and $link and $link ne $synccheck->{server}) { + push (@{$synccheck->{links}}, $link); + } +} + +sub _register +{ + my $server = shift; my $nick = lc(shift); my $sig = shift; + %{$synccheck->{names}->{$server}->{$nick}} = ( + NULL => 1, + op => 0, + voice => 0, + $sig => 1, + ) if (defined $synccheck); +} + +sub _isregister ($$) +{ + my $server = shift; my $nick = lc(shift); + return ((defined $synccheck and defined $synccheck->{names}->{$server}->{$nick}->{NULL}) ? 1 : 0); +} + +sub _isop ($$) +{ + my $server = shift; my $nick = lc(shift); + return ((_isregister($server, $nick) and $synccheck->{names}->{$server}->{$nick}->{op}) ? 1 : 0); +} + +sub _isvoice ($$) +{ + my $server = shift; my $nick = lc(shift); + return ((_isregister($server, $nick) and $synccheck->{names}->{$server}->{$nick}->{voice}) ? 1 : 0); +} + +sub _rec2mod ($) +{ + my $hash = shift; + my $mod = ($hash->{voice}) ? '+' : undef; $mod .= ($hash->{op}) ? '@' : undef; + return $mod; +} + +sub _reg2mod ($$) +{ + my $server = shift; my $nick = lc(shift); my $mod = undef; + if (_isregister($server, $nick)) { + $mod .= ($synccheck->{names}->{$server}->{$nick}->{voice}) ? '+' : ($synccheck->{names}->{$server}->{$nick}->{op}) ? '@' : ''; + } + return $mod; +} + +sub _errorregister ($$) { + my ($nick, $sig) = @_; my $retval = 1; + unless (Irssi::settings_get_bool("synccheck_show_all_errors")) { + $retval = ($synccheck->{registered_errors}->{$nick}->{$sig}) ? 0 : 1; + } + $synccheck->{registered_errors}->{$nick}->{$sig}++; + return $retval; +} + +sub _adderror ($$$$) +{ + my ($nick, $sig, $server, $error) = @_; + if (_errorregister $nick, $sig) { + push @{$synccheck->{errors}->{$server}}, $error; + } +} + +sub _flusherrors ($$) +{ + my ($local, $remote) = @_; + if ($#{$synccheck->{errors}->{$remote}} >= 0) { + _print($local, MSGLEVEL_CLIENTCRAP, "(error in synchronization will be shown only once for the first server where the error exists, but it can exists on more servers; if you want to show all errors use /set synccheck_show_all_errors On)") + if (!Irssi::settings_get_bool("synccheck_show_all_errors") and !$synccheck->{_info}++); + _print($local, MSGLEVEL_CLIENTCRAP, "%RPossible channel %n%_$synccheck->{name}%_%R desynced%n%_ $synccheck->{server} <-> $remote%_:"); + for (@{$synccheck->{errors}->{$remote}}) + { + _print($local, MSGLEVEL_CLIENTCRAP, "%_".sprintf("%03d", ++$synccheck->{_error}).".%_ $_"); + } + delete $synccheck->{errors}->{$remote}; + } +} + +sub _addnames +{ + my $remote = shift; return unless ($remote and defined $synccheck); + for (@_) + { + /^\@/ and substr($_, 0, 1) = "", _register($remote, $_, "op"), next; + /^\+/ and substr($_, 0, 1) = "", _register($remote, $_, "voice"), next; + _register($remote, $_, "NULL"); + } +} + +sub _numlinks +{ + return ((defined $synccheck and defined $synccheck->{links}) ? scalar(@{$synccheck->{links}}) : 0); +} + +sub tdiff ($) +{ + my $end = time(); my $start = shift; + return (($start and $start =~ /^\d+$/ and $start <= $end) ? ($end - $start) : 0); +} + +sub _synccheck ($) +{ + my $local = shift; my $remote = ${$synccheck->{links}}[$synccheck->{_tested}++]; + + _endof("End of sync-check (canceled)"), return + if (!$synccheck or !$local->channel_find($synccheck->{name})); + + unless ($remote) { + _print($local, MSGLEVEL_CLIENTCRAP, "%_Sync-check%_ in $synccheck->{name} ($synccheck->{tag}) %_finished in ".tdiff($synccheck->{time})." secs%_"); + _endof; return; + } + + _print($local, MSGLEVEL_CLIENTCRAP, "%K->%n checking $synccheck->{name}: $synccheck->{server} %_<-> $remote%_ %K[%n$synccheck->{_tested}/"._numlinks."%K]%n"); + + $local->redirect_event("names", 0, '', 1, undef, { + 'event 353' => 'redir names line', + 'event 366' => 'redir names done', + 'event 402', => 'redir names split', + '' => 'event empty' }); + + $local->send_raw("NAMES $synccheck->{channel} :$remote"); +} + +sub _test +{ + my ($local, $remote) = @_; + + unless (_isregister $remote, $local->{nick}) { + _adderror($local->{nick}, "notexsit", $remote, "%_you\'re%_ not in channel $synccheck->{name} on $remote"); + _flusherrors($local, $remote); + delete $synccheck->{names}->{$remote}; + return; + } + + my $channel = $local->channel_find($synccheck->{name}); + _endof, return unless $channel; + + my %orig = (); map($orig{lc($_->{nick})} = $_, $channel->nicks()); + + foreach my $nick (keys %{$synccheck->{names}->{$remote}}) + { + if (!$orig{$nick}) { + _adderror($nick, "notexist", $remote, "%_*notexist%_($synccheck->{server}) %_!= "._reg2mod($remote, $nick)."$nick%_($remote)"); + $orig{$nick} = 0; next; + } + + my $op = _isop $remote, $nick; my $voice = _isvoice $remote, $nick; + if ($orig{$nick}->{op} != $op) { + my $mod1 = _rec2mod($orig{$nick}); my $mod2 = _reg2mod($remote, $nick); + _adderror($nick, "op", $remote, "%_$mod1%_$nick($synccheck->{server}) %_!= $mod2%_$nick($remote)"); + + } elsif (!$op and $orig{$nick}->{voice} != $voice) { + my $mod1 = _rec2mod($orig{$nick}); my $mod2 = _reg2mod($remote, $nick); + _adderror($nick, "voice", $remote, "%_$mod1%_$nick($synccheck->{server}) %_!= $mod2%_$nick($remote)"); + } + $orig{$nick} = 0; + } + delete $synccheck->{names}->{$remote}; + + foreach my $nick (keys %orig) + { + next unless $orig{$nick}; + _adderror($nick, "notexist", $remote, _rec2mod($orig{$nick})."%_$nick%_($synccheck->{server}) %_!= *notexist%_($remote)"); + } + + _flusherrors($local, $remote); + _synccheck $local; +} + +Irssi::command_bind 'sync-check' => sub +{ + my $usage = "/%_sync-check%_ [%_channel%_ (%_servers%_)|%_-stop%_]"; + + unless ($_[1] and $_[1]->{type} eq 'SERVER' and $_[1]->{connected}) { + Irssi::print("Not connected to server"); + return; + } + + if (defined $synccheck) { + if ($_[0] !~ /^-stop/) { + Irssi::print("Sync-check already running " . tdiff($synccheck->{time}) . " secs ago for channel %_$synccheck->{name}%_, wait..."); + } else { + _endof("%_Stopping%_ sync-checker for channel %_$synccheck->{name}%_ in $synccheck->{tag}") + } + return; + } + + return unless _new($_[1]); + + foreach (split / +/, $_[0]) + { + /^-yes/i and $synccheck->{_yes} = 1, next; + /^-stop$/ and _endof("Not running any sync-checker"), return; + /^-/ and _endof("Unknown argument: %_$_%_, usage: $usage"), return; + if ($_[1]->ischannel($_)) { _setchan $_; } else { _addlink $_; } + } + + if ($synccheck->{channel} and !$_[1]->channel_find($synccheck->{channel})) { + _endof("You\'re not in channel %_$synccheck->{name}%_"); return; + } elsif (!$synccheck->{channel}) { + if ($_[2] and $_[2]->{type} eq 'CHANNEL') { + _setchan $_[2]->{name}; + } else { + _endof("Not joined to any channel"); return; + } + } + + if (!_numlinks) { + _endof("Doing this is not a good idea. Add -YES option to command if you really mean it"), return unless ($synccheck->{_yes}); + + _print($_[1], MSGLEVEL_CLIENTCRAP, "Checking for %_links%_ from %_$synccheck->{server}%_ in %_$synccheck->{tag}%_, wait..."); + $_[1]->redirect_event('links', 0, '', 1, undef, { + 'event 364' => 'redir links line', + 'event 365' => 'redir links done', + '' => 'event empty' }); + $_[1]->send_raw('LINKS :*'); + + } else { + if (_numlinks) { + _print($_[1], MSGLEVEL_CLIENTCRAP, "%_Checking channel $synccheck->{name} synchronization%_ in: $synccheck->{server} %_<->%_ @{$synccheck->{links}}. This will take a while.."); + _synccheck $_[1]; + } + } +}; + +Irssi::Irc::Server::redirect_register( + "links", 0, 0, + { "event 364" => 1, }, + { "event 402" => 1, "event 263" => 1, "event 365" => 1, }, + undef, +); + +Irssi::Irc::Server::redirect_register( + "names", 0, 0, + { "event 353" => 1, }, + { "event 366" => 1, + "event 402" => 1, }, + undef, +); + +Irssi::signal_add 'redir links line' => sub { + $_[1] =~ /(.*) (.*) (.*) :(.*)/; + _addlink $2; +}; + +Irssi::signal_add 'redir links done' => sub { + if (_numlinks) { + _print($_[0], MSGLEVEL_CLIENTCRAP, "%_Checking channel $synccheck->{name} synchronization%_ in: $synccheck->{server} %_<->%_ @{$synccheck->{links}}. This will take a while.."); + _synccheck $_[0]; + } +}; + +Irssi::signal_add 'redir names line' => sub { + $_[1] =~ /(.*) (.*) :(.*)/; + _addnames($_[2], split(" ", $3)) if (defined $synccheck and lc($2) eq $synccheck->{channel}); +}; + +Irssi::signal_add 'redir names done' => sub +{ + $_[1] =~ /(.*) (.*) :(.*)/; + _test($_[0], $_[2]) if (defined $synccheck and lc($2) eq $synccheck->{channel});; +}; + +Irssi::signal_add 'redir names split' => sub +{ + $_[1] =~ /(.*) (.*) :(.*)/; + _print($_[0], MSGLEVEL_CLIENTCRAP, "%K->%n%_ $2%_: cannot find link (".lc($3)."), skipping"); + _synccheck $_[0]; +}; + +Irssi::settings_add_bool('misc', 'synccheck_show_all_errors', 0); + |