archive

- Random tools & helpful resources for IRC
git clone git://git.acid.vegas/archive.git
Log | Files | Refs | Archive

parrot.pl (11976B)

      1 use strict;
      2 use warnings;
      3 
      4 no strict 'subs';
      5 
      6 my $SCRIPT_NAME = 'parrot';
      7 my $SCRIPT_AUTHOR = 'The Krusty Krab <wowaname@volatile.ch>';
      8 my $SCRIPT_VERSION = '1.0';
      9 my $SCRIPT_LICENCE = 'Public domain';
     10 my $SCRIPT_DESC = 'Relay channel messages and modes';
     11 
     12 # %cbs{server}{hook_name} = $hook_ptr:
     13 #     stores hook pointers to unhook() on exit
     14 # %chans{server}{channel} = @groups:
     15 #     stores groups associated with a channel
     16 # %groups{groupname}{server}{channel} = $flags:
     17 #     stores channels associated with a group, as well as the channel's flags
     18 # $READ, $STAT, $MODE:
     19 #     flags for -read, -stat, -mode switches
     20 our (%cbs, %chans, %groups);
     21 our ($READ, $STAT, $MODE) = (0x1, 0x2, 0x4);
     22 our $confpath;
     23 
     24 sub servchan
     25 {
     26 	my $buffer = shift;
     27 	return (lc weechat::buffer_get_string($buffer, 'localvar_server'),
     28 		lc weechat::buffer_get_string($buffer, 'localvar_channel'));
     29 }
     30 
     31 sub ircbuf { weechat::buffer_search('irc', "(?i)".(join '.', @_)) }
     32 
     33 sub getgroup
     34 {
     35 	my ($server, $channel) = @_;
     36 	my @ret;
     37 
     38 	for my $group (@{ $chans{$server}{$channel} }) {
     39 	for my $to_serv (keys %{ $groups{$group} }) {
     40 	for my $to_chan (keys %{ $groups{$group}{$to_serv} }) {
     41 		# don't send to myself
     42 		next if $to_serv eq $server and $to_chan eq $channel;
     43 		push @ret, [$to_serv, $to_chan, $groups{$group}{$to_serv}{$to_chan}, $group]
     44 	} } }
     45 
     46 	return @ret;
     47 }
     48 
     49 sub sendto
     50 {
     51 	my ($server, $command) = @_;
     52 	weechat::hook_signal_send('irc_input_send',
     53 		weechat::WEECHAT_HOOK_SIGNAL_STRING,
     54 		"$server;;1;;$command");
     55 }
     56 
     57 sub add_relay
     58 {
     59 	my ($groupname, $server, $channel, $flags) = @_;
     60 	return if exists $cbs{$server};
     61 	push @{ $chans{$server}{$channel} }, $groupname;
     62 	$groups{$groupname}{$server}{$channel} = $flags;
     63 	$cbs{$server}{PRIVMSG} =
     64 		weechat::hook_signal("$server,irc_raw_in_privmsg", 'irc_privmsg_notice', '');
     65 	$cbs{$server}{NOTICE} =
     66 		weechat::hook_signal("$server,irc_raw_in_notice", 'irc_privmsg_notice', '');
     67 	$cbs{$server}{OUT_PRIVMSG} =
     68 		weechat::hook_signal("$server,irc_out1_privmsg", 'ircout_privmsg_notice', '');
     69 	$cbs{$server}{OUT_NOTICE} =
     70 		weechat::hook_signal("$server,irc_out1_notice", 'ircout_privmsg_notice', '');
     71 	if ($flags & $STAT) {
     72 		$cbs{$server}{JOIN} =
     73 			weechat::hook_signal("$server,irc_raw_in_join", 'irc_join', '');
     74 		$cbs{$server}{PART} =
     75 			weechat::hook_signal("$server,irc_raw_in_part", 'irc_part', '');
     76 		$cbs{$server}{KICK} =
     77 			weechat::hook_signal("$server,irc_raw_in_kick", 'irc_kick', '');
     78 		$cbs{$server}{NICK} =
     79 			weechat::hook_signal("$server,irc_raw_in_nick", 'irc_nick', '');
     80 		$cbs{$server}{QUIT} =
     81 			weechat::hook_signal("$server,irc_raw_in_quit", 'irc_quit', '');
     82 	}
     83 	if ($flags & $MODE) {
     84 #		$cbs{$server}{MODE} =
     85 #			weechat::hook_signal("$server,irc_raw_in_mode", 'irc_mode', '');
     86 		$cbs{$server}{TOPIC} =
     87 			weechat::hook_signal("$server,irc_raw_in_topic", 'irc_topic', '');
     88 	}
     89 }
     90 
     91 sub read_conf
     92 {
     93 	open FH, '<', $confpath or weechat::print('', weechat::prefix('error').
     94 		"Error opening $confpath for reading: $!"), return;
     95 	while (<FH>) {
     96 		chomp;
     97 		add_relay(split ' ');
     98 	}
     99 	close FH;
    100 }
    101 
    102 sub write_conf
    103 {
    104 	open FH, '>', $confpath or weechat::print('', weechat::prefix('error').
    105 		"Error opening $confpath for writing: $!"), return;
    106 	for my $server (keys %chans) {
    107 	for my $channel (keys %{ $chans{$server} }) {
    108 	for my $group (@{ $chans{$server}{$channel} }) {
    109 		my $flags = $groups{$group}{$server}{$channel};
    110 		print FH "$group $server $channel $flags\n";
    111 	} } }
    112 	close FH;
    113 }
    114 
    115 sub irc_privmsg_notice
    116 {
    117 	my (undef, $server, $cmd, $nick, $channel, $message) = (shift,
    118 		shift =~ /(.+),irc_raw_in_(privmsg|notice)/i,
    119 		shift =~ /:([^! ]*)[^ ]* [^ ]+ ([^ ]+) :?(.*)/i);
    120 	($server, $channel) = (lc $server, lc $channel);
    121 	return weechat::WEECHAT_RC_OK unless exists $chans{$server}{$channel};
    122 
    123 	for (getgroup($server, $channel)) {
    124 		my ($to_serv, $to_chan, $flags, undef) = @$_;
    125 		next if $flags & $READ;
    126 		next unless ircbuf("$to_serv.$to_chan");
    127 		if ($message =~ /^\x01ACTION /i) {
    128 			$message =~ s/^\x01ACTION |\x01$//g;
    129 			sendto($to_serv, "/msg $to_chan * \x02$nick\x0f $message");
    130 			next;
    131 		}
    132 		my $prefix = lc $cmd eq 'notice' ? "[\x02$nick\x0f]" : "<\x02$nick\x0f>";
    133 		sendto($to_serv, "/msg $to_chan $prefix $message");
    134 	}
    135 
    136 	return weechat::WEECHAT_RC_OK;
    137 }
    138 
    139 sub ircout_privmsg_notice
    140 {
    141 	my (undef, $server, $cmd, $channel, $message) = (shift,
    142 		shift =~ /(.*),irc_out1_(privmsg|notice)/i,
    143 		shift =~ /[^ ]+ ([^ ]+) :?(.*)/i);
    144 	($server, $channel) = (lc $server, lc $channel);
    145 	return weechat::WEECHAT_RC_OK unless exists $chans{$server}{$channel};
    146 
    147 	for (getgroup($server, $channel)) {
    148 		my ($to_serv, $to_chan, $flags, undef) = @$_;
    149 		next if $flags & $READ;
    150 		next unless ircbuf("$to_serv.$to_chan");
    151 		my $prefix = lc $cmd eq 'notice' ? 'notice' : 'msg';
    152 		if ($message =~ /^\x01ACTION /i) {
    153 			$message =~ s/^\x01ACTION |\x01$//g;
    154 			sendto($to_serv, "/$prefix $to_chan \x01ACTION $message\x01");
    155 			next;
    156 		}
    157 		sendto($to_serv, "/$prefix $to_chan $message");
    158 	}
    159 
    160 	return weechat::WEECHAT_RC_OK;
    161 }
    162 
    163 sub irc_join
    164 {
    165 	my (undef, $server, $nick, $host, $channel) = (shift,
    166 		shift =~ /(.+),irc_raw_in_join/i,
    167 		shift =~ /:([^! ]*)([^ ]*) join :?([^ ]+)/i);
    168 	($server, $channel) = (lc $server, lc $channel);
    169 	return weechat::WEECHAT_RC_OK unless exists $chans{$server}{$channel};
    170 
    171 	for (getgroup($server, $channel)) {
    172 		my ($to_serv, $to_chan, $flags, undef) = @$_;
    173 		next unless $flags & $STAT;
    174 		next if $flags & $READ;
    175 		next unless ircbuf("$to_serv.$to_chan");
    176 		sendto($to_serv, "/notice $to_chan \x02$nick\x0f$host joined $server/$channel\x0f");
    177 	}
    178 
    179 	return weechat::WEECHAT_RC_OK;
    180 }
    181 
    182 sub irc_part
    183 {
    184 	my (undef, $server, $nick, $channel, $message) = (shift,
    185 		shift =~ /(.+),irc_raw_in_part/i,
    186 		shift =~ /:([^! ]*)[^ ]* part ([^ ]+) ?:?(.*)/i);
    187 	($server, $channel) = (lc $server, lc $channel);
    188 	return weechat::WEECHAT_RC_OK unless exists $chans{$server}{$channel};
    189 
    190 	for (getgroup($server, $channel)) {
    191 		my ($to_serv, $to_chan, $flags, undef) = @$_;
    192 		next unless $flags & $STAT;
    193 		next if $flags & $READ;
    194 		next unless ircbuf("$to_serv.$to_chan");
    195 		sendto($to_serv, "/notice $to_chan \x02$nick\x0f left $server/$channel\x0f: $message");
    196 	}
    197 
    198 	return weechat::WEECHAT_RC_OK;
    199 }
    200 
    201 sub irc_kick
    202 {
    203 	my (undef, $server, $nick, $channel, $target, $message) = (shift,
    204 		shift =~ /(.+),irc_raw_in_kick/i,
    205 		shift =~ /:([^! ]*)[^ ]* kick ([^ ]+) ([^ ]+) :?(.*)/i);
    206 	($server, $channel) = (lc $server, lc $channel);
    207 	return weechat::WEECHAT_RC_OK unless exists $chans{$server}{$channel};
    208 
    209 	for (getgroup($server, $channel)) {
    210 		my ($to_serv, $to_chan, $flags, undef) = @$_;
    211 		next unless $flags & $STAT;
    212 		next if $flags & $READ;
    213 		next unless ircbuf("$to_serv.$to_chan");
    214 		sendto($to_serv, "/notice $to_chan \x02$nick\x0f kicked $target\x0f from $server/$channel\x0f: $message");
    215 	}
    216 
    217 	return weechat::WEECHAT_RC_OK;
    218 }
    219 
    220 sub irc_nick
    221 {
    222 	my (undef, $server, $nick, $newnick) = (shift,
    223 		shift =~ /(.+),irc_raw_in_nick/i,
    224 		shift =~ /:([^! ]*)[^ ]* nick :?(.*)/i);
    225 
    226 	for my $channel (keys %{ $chans{$server} }) {
    227 	my $iptr = weechat::infolist_get('irc_nick', '', "$server,$channel,$nick");
    228 	next unless $iptr;
    229 	weechat::infolist_free($iptr);
    230 	for (getgroup($server, $channel)) {
    231 		my ($to_serv, $to_chan, $flags, undef) = @$_;
    232 		next unless $flags & $STAT;
    233 		next if $flags & $READ;
    234 		next unless ircbuf("$to_serv.$to_chan");
    235 		sendto($to_serv, "/notice $to_chan \x02$nick\x0f is now \x02$newnick\x0f");
    236 	} }
    237 
    238 	return weechat::WEECHAT_RC_OK;
    239 }
    240 
    241 sub irc_quit
    242 {
    243 	my (undef, $server, $nick, $message) = (shift,
    244 		shift =~ /(.+),irc_raw_in_quit/i,
    245 		shift =~ /:([^! ]*)[^ ]* quit :?(.*)/i);
    246 
    247 	for my $channel (keys %{ $chans{$server} }) {
    248 	my $iptr = weechat::infolist_get('irc_nick', '', "$server,$channel,$nick");
    249 	next unless $iptr;
    250 	weechat::infolist_free($iptr);
    251 	for (getgroup($server, $channel)) {
    252 		my ($to_serv, $to_chan, $flags, undef) = @$_;
    253 		next unless $flags & $STAT;
    254 		next if $flags & $READ;
    255 		next unless ircbuf("$to_serv.$to_chan");
    256 		sendto($to_serv, "/notice $to_chan \x02$nick\x0f left $server: $message");
    257 	} }
    258 
    259 	return weechat::WEECHAT_RC_OK;
    260 }
    261 
    262 sub irc_mode
    263 {
    264 	my (undef, $server, $nick, $channel, $modes) = (shift,
    265 		shift =~ /(.+),irc_raw_in_mode/i,
    266 		shift =~ /:([^! ]*)[^ ]* mode ([^ ]+) (.*)/i);
    267 	($server, $channel) = (lc $server, lc $channel);
    268 	return weechat::WEECHAT_RC_OK unless exists $chans{$server}{$channel};
    269 
    270 	return weechat::WEECHAT_RC_OK;
    271 }
    272 
    273 sub irc_topic
    274 {
    275 	my (undef, $server, $nick, $channel, $message) = (shift,
    276 		shift =~ /(.+),irc_raw_in_topic/i,
    277 		shift =~ /:([^! ]*)[^ ]* topic ([^ ]+) :?([^ ]+)/i);
    278 	($server, $channel) = (lc $server, lc $channel);
    279 	weechat::print('',"$server $channel");
    280 	return weechat::WEECHAT_RC_OK unless exists $chans{$server}{$channel};
    281 	return weechat::WEECHAT_RC_OK if lc $nick eq lc weechat::info_get('irc_nick', $server);
    282 
    283 	for (getgroup($server, $channel)) {
    284 		my ($to_serv, $to_chan, $flags, undef) = @$_;
    285 		next unless $flags & $MODE;
    286 		next if $flags & $READ;
    287 		next unless ircbuf("$to_serv.$to_chan");
    288 		sendto($to_serv, "/topic $to_chan $message");
    289 	}
    290 
    291 	return weechat::WEECHAT_RC_OK;
    292 }
    293 
    294 sub cmd_parrot
    295 {
    296 	my (undef, $buffer, $command) = @_;
    297 	my ($server, $channel) = servchan($buffer);
    298 	my ($flags, $remove, $groupname) =
    299 	   (     0,       0,         '');
    300 	for (split / +/, $command) {
    301 		/^-read$/   and ($flags |= $READ), next;
    302 		/^-stat$/   and ($flags |= $STAT), next;
    303 		/^-mode$/   and ($flags |= $MODE), next;
    304 		/^-remove$/ and ($remove = 1), next;
    305 		$groupname = $_; last;
    306 	}
    307 
    308 	unless ($groupname) {
    309 		if ($chans{$server}{$channel}) {
    310 			for (getgroup($server, $channel)) {
    311 				my ($to_serv, $to_chan, $flags, $group) = @$_;
    312 				my $flag_str = $flags ? ':' : '';
    313 				$flag_str .= ' readonly' if $flags & $READ;
    314 				$flag_str .= ' statusmsg' if $flags & $STAT;
    315 				$flag_str .= ' sendmodes' if $flags & $MODE;
    316 				weechat::print($buffer, weechat::prefix('server').
    317 					"Relaying to $to_serv/$to_chan in group $group$flag_str");
    318 			}
    319 		} else {
    320 			weechat::print($buffer, weechat::prefix('server').
    321 				"This channel is not being relayed");
    322 		}
    323 		return weechat::WEECHAT_RC_OK;
    324 	}
    325 
    326 	# clear hooks first (if they exist)
    327 	if (exists $cbs{$server}) {
    328 		weechat::unhook($cbs{$server}{$_}) for (keys %{ $cbs{$server} });
    329 		delete $cbs{$server};
    330 	}
    331 	@{ $chans{$server}{$channel} } =
    332 		grep { $_ ne $groupname } @{ $chans{$server}{$channel} };
    333 
    334 	if ($remove) {
    335 		delete $groups{$groupname}{$server}{$channel};
    336 		delete $groups{$groupname}{$server} unless $groups{$groupname}{$server};
    337 		delete $groups{$groupname} unless $groups{$groupname};
    338 		delete $chans{$server}{$channel} unless $chans{$server}{$channel};
    339 		delete $chans{$server} unless $chans{$server};
    340 
    341 		write_conf();
    342 		weechat::print($buffer, weechat::prefix('server').
    343 			"Removed relay from group $groupname");
    344 		return weechat::WEECHAT_RC_OK;
    345 	}
    346 
    347 	add_relay($groupname, $server, $channel, $flags);
    348 
    349 	write_conf();
    350 	weechat::print($buffer, weechat::prefix('server').
    351 		"Added relay to group $groupname");
    352 	return weechat::WEECHAT_RC_OK;
    353 }
    354 
    355 sub completion_groupnames
    356 {
    357 	my $completion = pop;
    358 	weechat::hook_completion_list_add($completion, $_, 0,
    359 		weechat::WEECHAT_LIST_POS_SORT) for keys %groups;
    360 }
    361 
    362 if (weechat::register($SCRIPT_NAME, $SCRIPT_AUTHOR, $SCRIPT_VERSION,
    363  $SCRIPT_LICENCE, $SCRIPT_DESC, '', '')) {
    364 	$confpath = weechat::info_get('weechat_dir', '') . '/parrot.db';
    365 	weechat::hook_completion('perl_parrot_groupname', 'parrot.pl group names',
    366 		'completion_groupnames', '');
    367 	weechat::hook_command('parrot', $SCRIPT_DESC,
    368 		"[-read] [-stat] [-mode] groupname\n".
    369 		"-remove",
    370 		"-read: relay from this channel to others, but do not relay to\n".
    371 		"       this channel\n".
    372 		"-stat: show status messages (join/part) in this channel\n".
    373 		"-mode: transfer modes to this channel, even if you are op".
    374 		"groupname: all channels with the same group name are relayed together\n".
    375 		"-remove: remove this channel from the relay group",
    376 		'-remove %(perl_parrot_groupname) %-'.
    377 		'||-read|-stat|-mode|%(perl_parrot_groupname)|%*',
    378 		'cmd_parrot', '');
    379 	read_conf();
    380 }