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); + | 
