summaryrefslogtreecommitdiffstats
path: root/scripts/synccheck.pl
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/synccheck.pl')
-rw-r--r--scripts/synccheck.pl346
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);
+