Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
347 lines (291 sloc) 9.74 KB
#
# 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);