summaryrefslogtreecommitdiffstats
path: root/scripts/linkchan.pl
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--scripts/linkchan.pl488
1 files changed, 488 insertions, 0 deletions
diff --git a/scripts/linkchan.pl b/scripts/linkchan.pl
new file mode 100644
index 0000000..34fb619
--- /dev/null
+++ b/scripts/linkchan.pl
@@ -0,0 +1,488 @@
+use strict;
+use vars qw($VERSION %IRSSI);
+
+$VERSION = "1.5";
+%IRSSI =
+(
+ authors => 'Marcin \'Qrczak\' Kowalczyk',
+ contact => 'qrczak@knm.org.pl',
+ name => 'LinkChan',
+ description => 'Link several channels on serveral networks',
+ license => 'GNU GPL',
+ url => 'http://qrnik.knm.org.pl/~qrczak/irssi/linkchan.pl',
+);
+
+our %links;
+our $lock_own = 0;
+
+our $config = Irssi::get_irssi_dir . "/linkchan.cfg";
+
+Irssi::command_bind "link", sub
+{
+ my ($args, $server, $target) = @_;
+ Irssi::command_runsub "link", $args, $server, $target;
+};
+
+Irssi::command_bind "link add", sub
+{
+ my ($args, $server, $target) = @_;
+ unless ($args =~ m|^ *([^ /]+)/([^ ]+) +([^ /]+)/([^ ]+) *$|)
+ {
+ print CLIENTERROR "Usage: /link add <chatnet1>/<channel1> <chatnet2>/<channel2>";
+ return;
+ }
+ my ($chatnet1, $channel1, $chatnet2, $channel2) =
+ (lc $1, lc $2, lc $3, lc $4);
+ foreach my $link ([$chatnet1, $channel1], [$chatnet2, $channel2])
+ {
+ my ($chat1, $chan1) = @{$link};
+ if ($links{$chat1}{$chan1})
+ {
+ my ($chat2, $chan2) = @{$links{$chat1}{$chan1}};
+ print CLIENTERROR "Channel $chat1/$chan1 is already linked to $chat2/$chan2";
+ return;
+ }
+ }
+ $links{$chatnet1}{$channel1} = [$chatnet2, $channel2];
+ $links{$chatnet2}{$channel2} = [$chatnet1, $channel1];
+ print CLIENTNOTICE "Added link: $chatnet1/$channel1 <-> $chatnet2/$channel2";
+};
+
+Irssi::command_bind "link remove", sub
+{
+ my ($args, $server, $target) = @_;
+ unless ($args =~ m|^ *([^ /]+)/([^ ]+) *$|)
+ {
+ print CLIENTERROR "Usage: /link remove <chatnet>/<channel>";
+ return;
+ }
+ my ($chatnet1, $channel1) = (lc $1, lc $2);
+ unless ($links{$chatnet1}{$channel1})
+ {
+ print CLIENTERROR "Channel $chatnet1/$channel1 was not linked";
+ return;
+ }
+ my ($chatnet2, $channel2) = @{$links{$chatnet1}{$channel1}};
+ delete $links{$chatnet1}{$channel1};
+ delete $links{$chatnet2}{$channel2};
+ print CLIENTNOTICE "Removed link: $chatnet1/$channel1 <-> $chatnet2/$channel2";
+};
+
+Irssi::command_bind "link list", sub
+{
+ my ($args, $server, $target) = @_;
+ unless ($args =~ /^ *$/)
+ {
+ print CLIENTNOTICE "Usage: /link list";
+ return;
+ }
+ print CLIENTNOTICE "The following pairs of channels are linked:";
+ my %shown = ();
+ foreach my $chatnet1 (sort keys %links)
+ {
+ foreach my $channel1 (sort keys %{$links{$chatnet1}})
+ {
+ next if $shown{$chatnet1}{$channel1};
+ my ($chatnet2, $channel2) = @{$links{$chatnet1}{$channel1}};
+ print CLIENTNOTICE "$chatnet1/$channel1 <-> $chatnet2/$channel2";
+ $shown{$chatnet2}{$channel2} = 1;
+ }
+ }
+};
+
+sub save_config()
+{
+ open CONFIG, ">", $config;
+ foreach my $chatnet1 (keys %links)
+ {
+ foreach my $channel1 (keys %{$links{$chatnet1}})
+ {
+ my ($chatnet2, $channel2) = @{$links{$chatnet1}{$channel1}};
+ print CONFIG "$chatnet1/$channel1 $chatnet2/$channel2\n";
+ }
+ }
+ close CONFIG;
+}
+
+Irssi::signal_add "setup saved", sub
+{
+ my ($main_config, $auto) = @_;
+ save_config unless $auto;
+};
+
+sub load_config()
+{
+ %links = ();
+ open CONFIG, "<", $config or return;
+ while (<CONFIG>)
+ {
+ chomp;
+ next if /^ *$/ || /^#/;
+ unless (m|^ *([^ /]+)/([^ ]+) +([^ /]+)/([^ ]+) *$|)
+ {
+ print CLIENTERROR "Syntax error in $config: $_";
+ return;
+ }
+ my ($chatnet1, $channel1, $chatnet2, $channel2) =
+ (lc $1, lc $2, lc $3, lc $4);
+ $links{$chatnet1}{$channel1} = [$chatnet2, $channel2];
+ }
+}
+
+Irssi::signal_add "setup reread", \&load_config;
+
+sub message($$)
+{
+ my ($chan, $msg) = @_;
+ $lock_own = 1;
+ $chan->{server}->command("msg $chan->{name} $msg");
+ $lock_own = 0;
+}
+
+sub special_message($$)
+{
+ my ($chan, $msg) = @_;
+ message $chan, "-!- $msg";
+}
+
+sub special_message_for($$$)
+{
+ my ($chan, $nick, $msg) = @_;
+ message $chan,
+ (defined $nick ? "$nick: " : "") .
+ "-!- $msg";
+}
+
+sub channel_context($$)
+{
+ my ($server1, $channel1) = @_;
+ my $chatnet1 = lc $server1->{chatnet};
+ my $chan1 = $server1->channel_find($channel1) or return undef;
+ my $other = $links{$chatnet1}{lc $channel1} or return undef;
+ my ($chatnet2, $channel2) = @{$other};
+ my $server2 = Irssi::server_find_chatnet($chatnet2) or return;
+ my $chan2 = $server2->channel_find($channel2) or return;
+ return {
+ chatnet1 => $chatnet1,
+ server1 => $server1,
+ channel1 => $channel1,
+ chan1 => $chan1,
+ chatnet2 => $chatnet2,
+ server2 => $server2,
+ channel2 => $channel2,
+ chan2 => $chan2,
+ };
+}
+
+sub channel_contexts_with_nick($$)
+{
+ my ($server1, $nick1) = @_;
+ my $chatnet1 = lc $server1->{chatnet};
+ return () unless $links{$chatnet1};
+ my @contexts = ();
+ foreach my $channel1 (keys %{$links{$chatnet1}})
+ {
+ my $chan1 = $server1->channel_find($channel1) or next;
+ next unless $chan1->nick_find($nick1);
+ my ($chatnet2, $channel2) = @{$links{$chatnet1}{$channel1}};
+ my $server2 = Irssi::server_find_chatnet($chatnet2) or next;
+ my $chan2 = $server2->channel_find($channel2) or next;
+ push @contexts, {
+ chatnet1 => $chatnet1,
+ server1 => $server1,
+ channel1 => $channel1,
+ chan1 => $chan1,
+ chatnet2 => $chatnet2,
+ server2 => $server2,
+ channel2 => $channel2,
+ chan2 => $chan2,
+ };
+ }
+ return @contexts;
+}
+
+sub must_be_op($$)
+{
+ my ($context, $nick) = @_;
+ unless (defined $nick ?
+ $context->{chan1}->nick_find($nick)->{op} :
+ $context->{chan1}->{chanop})
+ {
+ special_message_for $context->{chan1}, $nick,
+ "You're not channel operator in $context->{channel1}";
+ return 0;
+ }
+ unless ($context->{chan2}->{chanop})
+ {
+ special_message_for $context->{chan1}, $nick,
+ "Sorry, I'm not channel operator in $context->{channel2}";
+ return 0;
+ }
+ return 1;
+}
+
+sub change_mode($$$)
+{
+ my ($context, $nick, $mode) = @_;
+ return unless must_be_op($context, $nick);
+ special_message $context->{chan2},
+ "mode/$context->{channel2} [$mode] by $nick"
+ if defined $nick;
+ $context->{server2}->command("mode $context->{channel2} $mode");
+}
+
+sub change_perms($$$$$$)
+{
+ my ($command, $dir, $mode, $context, $nick, $args) = @_;
+ my @nicks = split ' ', $args;
+ unless (@nicks)
+ {
+ special_message_for $context->{chan1}, $nick,
+ "Usage: \\$command <nicks>";
+ return;
+ }
+ change_mode $context, $nick, $dir . $mode x @nicks . " @nicks";
+}
+
+sub names($$$)
+{
+ my ($context, $nick, $args) = @_;
+ my @nicks = $context->{chan2}->nicks();
+ my @ops = grep {$_->{op}} @nicks;
+ my @voices = grep {!$_->{op} && $_->{voice}} @nicks;
+ my @normal = grep {!$_->{op} && !$_->{voice}} @nicks;
+ my @list = (
+ map ({['@', $_]} sort {lc $a cmp lc $b} map {$_->{nick}} @ops),
+ map ({['+', $_]} sort {lc $a cmp lc $b} map {$_->{nick}} @voices),
+ map ({[' ', $_]} sort {lc $a cmp lc $b} map {$_->{nick}} @normal));
+ my $max_width = 62 - length $context->{server1}->{nick};
+ my $rows = 1;
+ my @column_widths;
+ while ($rows < @list)
+ {
+ @column_widths = ();
+ my $width = 0;
+ my $i = 0;
+ while ($i < @list)
+ {
+ my $column_width = 0;
+ foreach my $j ($i .. $i+$rows-1)
+ {
+ last if $j >= @list;
+ my $len = length $list[$j][1];
+ $column_width = $len if $column_width < $len;
+ }
+ push @column_widths, $column_width;
+ $width += $column_width + 4;
+ $i += $rows;
+ }
+ last if $width - 1 <= $max_width;
+ ++$rows;
+ }
+ my @output;
+ foreach my $i (0..$#list)
+ {
+ $output[$i % $rows] .=
+ sprintf "[%s%*s] ",
+ $list[$i][0], -$column_widths[int ($i / $rows)], $list[$i][1];
+ }
+ foreach my $row (@output)
+ {
+ chop $row;
+ message $context->{chan1}, $row;
+ }
+}
+
+my %commands =
+(
+ mode => sub
+ {
+ my ($context, $nick, $args) = @_;
+ unless ($args =~ /^ +\* +(.*)$/ ||
+ $args =~ /^ +\Q$context->{channel2}\E +(.*)$/)
+ {
+ special_message_for $context->{chan1}, $nick,
+ "Usage: \\mode * <mode> [<mode parameters>]";
+ return;
+ }
+ change_mode $context, $nick, $1;
+ },
+ op => sub {&change_perms('op', '+', 'o', @_)},
+ deop => sub {&change_perms('deop', '-', 'o', @_)},
+ voice => sub {&change_perms('voice', '+', 'v', @_)},
+ devoice => sub {&change_perms('devoice', '-', 'v', @_)},
+ kick => sub
+ {
+ my ($context, $nick, $args) = @_;
+ unless ($args =~ /^ +([^ ]+)(| .*)$/)
+ {
+ special_message_for $context->{chan1}, $nick,
+ "Usage: \\kick <nicks> [<reason>]";
+ return;
+ }
+ my ($nicks, $reason) = ($1, $2);
+ $reason = $reason =~ /^ ?$/ ? " $nick" : " <$nick>$reason"
+ if defined $nick;
+ return unless must_be_op($context, $nick);
+ $context->{server2}->command("kick $context->{channel2} $nicks$reason");
+ },
+ names => \&names,
+);
+
+sub run_command($$$$)
+{
+ my ($context, $nick, $command, $args) = @_;
+ my $func = $commands{lc $command};
+ unless ($func)
+ {
+ special_message_for $context->{chan1}, $nick,
+ "Unknown command: $command";
+ return;
+ }
+ $func->($context, $nick, $args);
+}
+
+Irssi::signal_add "message public", sub
+{
+ my ($server1, $msg, $nick, $address, $channel1) = @_;
+ my $context = channel_context($server1, $channel1) or return;
+ if ($msg =~ /^\\([^ ]+)(| .*)$/)
+ {
+ Irssi::signal_continue @_;
+ run_command $context, $nick, $1, $2;
+ }
+ elsif ($msg =~ /^<.[^ ]+> /)
+ {
+ print CLIENTERROR
+ "Warning! Channels $context->{chatnet1}/$context->{channel1} " .
+ "and $context->{chatnet2}/$context->{channel2} are linked twice.";
+ Irssi::command "beep";
+ }
+ else
+ {
+ my $nk = $context->{chan1}->nick_find($nick);
+ my $perm = $nk->{op} ? '@' : $nk->{voice} ? '+' : ' ';
+ message $context->{chan2}, "<$perm$nick> $msg";
+ }
+};
+
+Irssi::signal_add "message own_public", sub
+{
+ my ($server1, $msg, $channel1) = @_;
+ return if $lock_own;
+ my $context = channel_context($server1, $channel1) or return;
+ if ($msg !~ s/^\\ // && $msg =~ /^\\([^ ]+)(| .*)$/)
+ {
+ Irssi::signal_continue @_;
+ run_command $context, undef, $1, $2;
+ }
+ else
+ {
+ message $context->{chan2}, $msg;
+ }
+};
+
+Irssi::signal_add "message irc action", sub
+{
+ my ($server1, $msg, $nick, $address, $channel1) = @_;
+ my $context = channel_context($server1, $channel1) or return;
+ message $context->{chan2}, " * $nick $msg";
+};
+
+Irssi::signal_add "message irc own_action", sub
+{
+ my ($server1, $msg, $channel1) = @_;
+ return if $lock_own;
+ my $context = channel_context($server1, $channel1) or return;
+ $lock_own = 1;
+ $context->{server2}->command("action $context->{channel2} $msg");
+ $lock_own = 0;
+};
+
+Irssi::signal_add "message join", sub
+{
+ my ($server1, $channel1, $nick, $address) = @_;
+ my $context = channel_context($server1, $channel1) or return;
+ special_message $context->{chan2},
+ "$nick [$address] has joined $channel1";
+};
+
+Irssi::signal_add "message part", sub
+{
+ my ($server1, $channel1, $nick, $address, $reason) = @_;
+ my $context = channel_context($server1, $channel1) or return;
+ special_message $context->{chan2},
+ "$nick [$address] has left $context->{channel1} [$reason]";
+};
+
+Irssi::signal_add "message quit", sub
+{
+ my ($server1, $nick, $address, $reason) = @_;
+ foreach my $context (channel_contexts_with_nick($server1, $nick))
+ {
+ special_message $context->{chan2},
+ "$nick [$address] has quit [$reason]";
+ }
+};
+
+Irssi::signal_add "message topic", sub
+{
+ my ($server1, $channel1, $topic, $nick, $address) = @_;
+ return if $nick eq $server1->{nick};
+ my $context = channel_context($server1, $channel1) or return;
+ if ($topic eq "")
+ {
+ special_message $context->{chan2},
+ "Topic unset by $nick on $context->{channel1}";
+ $context->{server2}->command("topic -delete $context->{channel2}");
+ }
+ else
+ {
+ special_message $context->{chan2},
+ "$nick changed the topic of $context->{channel1} to: $topic";
+ $context->{server2}->command("topic $context->{channel2} $topic");
+ }
+};
+
+Irssi::signal_add "message nick", sub
+{
+ my ($server1, $newnick, $oldnick, $address) = @_;
+ foreach my $context (channel_contexts_with_nick($server1, $newnick))
+ {
+ special_message $context->{chan2},
+ "$oldnick is now known as $newnick";
+ }
+};
+
+Irssi::signal_add "message own_nick", sub
+{
+ my ($server1, $newnick, $oldnick, $address) = @_;
+ foreach my $context (channel_contexts_with_nick($server1, $newnick))
+ {
+ next if $context->{chatnet1} eq $context->{chatnet2};
+ special_message $context->{chan2},
+ "$oldnick is now known as $newnick";
+ }
+};
+
+Irssi::signal_add "message kick", sub
+{
+ my ($server1, $channel1, $nick, $kicker, $address, $reason) = @_;
+ my $context = channel_context($server1, $channel1) or return;
+ special_message $context->{chan2},
+ "$nick was kicked from $context->{channel1} " .
+ "by $kicker [$reason]";
+};
+
+Irssi::signal_add "event mode", sub
+{
+ my ($server1, $data, $nick) = @_;
+ $data =~ /^([^ ]*) (.*)$/ or return;
+ my ($channel1, $mode) = ($1, $2);
+ my $context = channel_context($server1, $channel1) or return;
+ special_message $context->{chan2},
+ "mode/$context->{channel1} [$mode] by $nick";
+};
+
+load_config;
+