random

- collection of un-sorted bollocks
git clone git://git.acid.vegas/random.git
Log | Files | Refs | Archive

commit a68a59bf5e6cb64bfdf97a5ed2ac34df193b7ade
parent ba071d718d49016afdeb4b270279fea6e74447d1
Author: acidvegas <acid.vegas@acid.vegas>
Date: Thu, 18 May 2023 18:13:29 -0400

Added opals weechat scripts

Diffstat:
Airc/weechat/antifuck.pl | 369+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Airc/weechat/banner.pl | 332+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Airc/weechat/colo.py | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Airc/weechat/coloconv.pl | 45+++++++++++++++++++++++++++++++++++++++++++++
Airc/weechat/hueg.pl | 1255+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Airc/weechat/masshl.py | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Airc/weechat/parrot.pl | 380+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Airc/weechat/play.pl | 156+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Airc/weechat/prismx.py | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Airc/weechat/sighup.pl | 22++++++++++++++++++++++
Airc/weechat/snomasks.pl | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

11 files changed, 3031 insertions(+), 0 deletions(-)

diff --git a/irc/weechat/antifuck.pl b/irc/weechat/antifuck.pl
@@ -0,0 +1,369 @@
+# Released into the Public Domain
+
+use strict;
+use warnings;
+
+no strict 'subs';
+
+my $SCRIPT_NAME = 'antifuck';
+my $SCRIPT_AUTHOR = 'The Krusty Krab <wowaname@volatile.ch>';
+my $SCRIPT_VERSION = '1.1';
+my $SCRIPT_LICENCE = 'Public domain';
+my $SCRIPT_DESC = 'Defend against forcejoins (e.g. from fuckyou.pl) and '.
+	'forceparts (e.g. from /remove)';
+
+my %OPTIONS = (
+	autopart => ['Whether to automatically part forcejoined channels. '.
+		'You can always do this manually with /antifuck part', '0'],
+	delay => ['Delay in milliseconds to wait before autoparting', '5000'],
+	forward => ['Whether to allow channel forwards (+f on freenode)', '1'],
+	ignore => ['Servers to ignore (e.g. for bouncers), separated by comma', ''],
+	nobufs => ['If 1, do not create buffers for forcejoined channels', '0'],
+	timeout =>
+		['Delay in milliseconds to wait for server to send JOIN after join',
+		'60000'],
+	);
+
+# %channels: channels we joined and received JOIN / NAMES for
+# %zombie: channels we joined but aren't yet in
+# %part: channels we were forced into and will part soon
+# %partbuf: buffers belonging to parted channels, we'll close these on
+#           /antifuck part
+our (%channels, %zombie, %part, %partbuf, $fuckbuf, $timeout_cb, $gc_cb);
+
+if (weechat::register($SCRIPT_NAME, $SCRIPT_AUTHOR, $SCRIPT_VERSION,
+ $SCRIPT_LICENCE, $SCRIPT_DESC, '', '')) {
+	weechat::hook_command('antifuck', $SCRIPT_DESC, 'part', <<'HELP',
+This script defends against forced joins, such as from irssi's fuckyou.pl or
+from channel forwards, as well as forced parts, such as from the /remove
+command. You can configure certain behaviour using the options under
+"plugins.var.perl.antifuck.*". Configure rejoin-on-/remove with the
+irc.server_default.autorejoin and .autorejoin_delay commands.
+
+Running "/antifuck part" will close all forcejoined channels and part them where
+appropriate.
+HELP
+		'part', 'cmd_antifuck', '');
+	weechat::hook_signal('irc_server_connected', 'irc_connect', '');
+	weechat::hook_signal('irc_server_disconnected', 'irc_disconnect', '');
+	weechat::hook_signal('irc_channel_opened', 'buffer_opened', '');
+	weechat::hook_signal('buffer_closed', 'buffer_closed', '');
+	weechat::hook_signal('*,irc_out1_join', 'client_join', '');
+	weechat::hook_signal('*,irc_out1_part', 'client_part', '');
+	weechat::hook_signal('*,irc_raw_in_001', 'irc_001', '');
+	weechat::hook_signal('*,irc_raw_in_470', 'irc_470', '');
+	weechat::hook_modifier('irc_in_366', 'irc_366', '');
+	weechat::hook_modifier('irc_in_join', 'irc_join', '');
+	weechat::hook_modifier('irc_in_part', 'irc_part', '');
+
+	for my $option (keys %OPTIONS) {
+		weechat::config_set_plugin($option, $OPTIONS{$option}[1])
+		 unless weechat::config_is_set_plugin($option);
+		weechat::config_set_desc_plugin($option, $OPTIONS{$option}[0]);
+	}
+
+	my $iptr = weechat::infolist_get('buffer', '', '');
+
+	while (weechat::infolist_next($iptr)) {
+		next unless weechat::infolist_string($iptr, 'plugin_name') eq 'irc';
+		my $buf = weechat::infolist_pointer($iptr, 'pointer');
+		$channels{
+			lc weechat::buffer_get_string($buf, 'localvar_server')}{
+			lc weechat::buffer_get_string($buf, 'localvar_channel')} = 1;
+	}
+	weechat::infolist_free($iptr);
+}
+
+sub mynick
+{
+	my ($buf, $nick) = ($_[0], $_[1]);
+
+	return lc weechat::buffer_get_string($buf, 'localvar_nick') eq lc $nick;
+}
+
+sub ignored
+{
+	my $server = shift;
+	my $ignore_conf = lc weechat::config_get_plugin('ignore');
+
+	return $ignore_conf =~ /(^|,)$server($|,)/;
+}
+
+sub nobufs { weechat::config_get_plugin('nobufs') }
+
+sub ircbuf { weechat::buffer_search('irc', "(?i)".(join '.', @_)) }
+sub ircparse { weechat::info_get_hashtable(irc_message_parse =>
+	{ message => shift }) }
+
+sub servchan
+{
+	my $buf = shift;
+
+	return (lc weechat::buffer_get_string($buf, 'localvar_server'),
+		lc weechat::buffer_get_string($buf, 'localvar_channel'));
+}
+
+sub reset_gc
+{
+	weechat::unhook($gc_cb) if $gc_cb;
+	$gc_cb = weechat::hook_timer(weechat::config_get_plugin('timeout'), 0, 1,
+		'run_gc', '');
+}
+
+sub cmd_antifuck
+{
+	my (undef, $buffer, $args) = @_;
+
+	if ($args eq 'part') {
+		# TODO: we really need to spend more time here making sure we send the
+		# fewest PARTs possible, a la irc_join_delay
+		weechat::buffer_close($fuckbuf);
+	}
+
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub fuckbuf_input { return weechat::WEECHAT_RC_OK; }
+
+sub fuckbuf_close
+{
+	weechat::buffer_close($_) for (keys %partbuf);
+	%partbuf = ();
+	$fuckbuf = '';
+
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub irc_connect
+{
+	my $server = pop;
+	my ($autojoin) = (weechat::config_string(weechat::config_get(
+		"irc.server.$server.autojoin")) =~ /^([^ ]*)/);
+
+	$zombie{$server}{$_} = 1 for (split ',', lc($autojoin));
+
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub irc_disconnect
+{
+	my $server = pop;
+
+	$server = lc $server;
+	delete $channels{$server};
+	delete $zombie{$server};
+	delete $part{$server};
+
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub buffer_opened {
+	my $buffer = pop;
+	my ($server, $channel) = servchan($buffer);
+	return weechat::WEECHAT_RC_OK if exists $channels{$server}{$channel};
+	return weechat::WEECHAT_RC_OK if ignored($server);
+
+	$fuckbuf = weechat::buffer_new(
+		'antifuck',
+		'fuckbuf_input',
+		'',
+		'fuckbuf_close',
+		''
+		) unless $fuckbuf;
+
+	weechat::buffer_merge($buffer, $fuckbuf);
+	#return weechat::WEECHAT_RC_OK unless weechat::config_get_plugin('autopart');
+
+	$partbuf{$buffer} = 1;
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub buffer_closed {
+	my $buffer = pop;
+
+	delete $partbuf{$buffer};
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub client_join
+{
+	my (undef, $server, $channel) = (shift,
+		shift =~ /(.+),irc_out1_join/i,
+		shift =~ /^join :?([^ ]*)/i);
+	($server, $channel) = (lc $server, lc $channel);
+
+	reset_gc();
+
+	($_ eq '0' ? %{$channels{$server}} = () : $zombie{$server}{$_} = 1)
+		for (split ',', $channel);
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub client_part
+{
+	my (undef, $server, $channel) = (shift,
+		shift =~ /(.+),irc_out1_part/i,
+		shift =~ /^part ([^ ]*)/i);
+	($server, $channel) = (lc $server, lc $channel);
+
+	delete $channels{$server}{$_} for (split ',', $channel);
+	return weechat::WEECHAT_RC_OK;
+}
+
+# RPL_WELCOME
+sub irc_001
+{
+	my (undef, $server, $message) = (shift,
+		shift =~ /(.+),irc_raw_in_001/, shift);
+
+	$server = lc $server;
+	return weechat::WEECHAT_RC_OK unless $message =~ / :- Welcome to ZNC -$/;
+
+	my $ignore_conf = lc weechat::config_get_plugin('ignore');
+	return weechat::WEECHAT_RC_OK if $ignore_conf =~ /(^|,)$server($|,)/;
+
+	weechat::config_set_plugin('ignore', "$ignore_conf,$server");
+
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub irc_join
+{
+	my ($server, $message, $msghash) = (lc $_[2], $_[3], ircparse($_[3]));
+	my ($nick, $channel) = ($msghash->{nick}, lc $msghash->{channel});
+	my $buffer = ircbuf("$server.$channel");
+
+	return $message if exists $channels{$server}{$channel};
+	if (exists $zombie{$server}{$channel} || ignored($server)) {
+		delete $zombie{$server}{$channel};
+		$channels{$server}{$channel} = 1;
+		return $message;
+	}
+	# XXX return $message unless mynick($buffer, $nick);
+
+	$part{$server}{$channel} = 1;
+	$timeout_cb = weechat::hook_timer(
+		weechat::config_get_plugin('delay'), 0, 1, 'irc_join_delay', $buffer)
+		unless $timeout_cb || !weechat::config_get_plugin('autopart');
+
+	return $message unless nobufs();
+
+	$fuckbuf = weechat::buffer_new(
+		'antifuck',
+		'fuckbuf_input',
+		'',
+		'fuckbuf_close',
+		''
+		) unless $fuckbuf;
+	weechat::print($fuckbuf, weechat::prefix('join').
+		weechat::color('irc.color.message_join').
+		'You were forced to join '.weechat::color('chat_channel').$channel.
+		weechat::color('irc.color.message_join').', leaving');
+
+	return '';
+}
+
+# RPL_ENDOFNAMES
+sub irc_366
+{
+	my ($server, $message) = ($_[2], $_[3]);
+	my ($nick, $channel) = $message =~ /^:[^ ]* 366 ([^ ]*) ([^ ]*)/i;
+	my $buffer = ircbuf("$server.$channel");
+	($server, $channel) = (lc $server, lc $channel);
+
+	return $message if exists $channels{$server}{$channel};
+	return '' if nobufs();
+
+	weechat::print($buffer, weechat::prefix('network').
+		'Forcejoined, not syncing modes');
+
+	return '';
+}
+
+# ERR_LINKCHANNEL
+sub irc_470
+{
+	my (undef, $server, $oldchan, $newchan) = (shift,
+		shift =~ /(.+),irc_raw_in_470/,
+		shift =~ /^:[^ ]* 470 [^ ]+ ([^ ]+) ([^ ]+)/);
+	($server, $oldchan, $newchan) = (lc $server, lc $oldchan, lc $newchan);
+
+	delete $channels{$server}{$oldchan};
+	$channels{$server}{$newchan} = 1 if weechat::config_get_plugin('forward');
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub irc_join_delay
+{
+	my $buffer = shift;
+
+	for my $server (keys %part) {
+		my $chans = '';
+
+		for my $chan (keys %{$part{$server}}) {
+			if (length($chans) + length($chan) > 500) {
+				weechat::hook_signal_send('irc_input_send',
+					weechat::WEECHAT_HOOK_SIGNAL_STRING,
+					"$server;;priority_low;;/part $chans");
+				$chans = '';
+			}
+
+			$chans .= "$chan,";
+		}
+
+		weechat::hook_signal_send('irc_input_send',
+			weechat::WEECHAT_HOOK_SIGNAL_STRING,
+			"$server;;priority_low;;/part $chans");
+	}
+	$timeout_cb = '';
+	%part = ();
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub run_gc
+{
+	%zombie = ();
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub irc_part
+{
+	my ($server, $message, $msghash) = ($_[2], $_[3], ircparse($_[3]));
+	my ($arj, $arj_delay, $arjd, $arjd_delay) = (
+		weechat::config_get("irc.server.$server.autorejoin"),
+		weechat::config_get("irc.server.$server.autorejoin_delay"),
+		weechat::config_get("irc.server_default.autorejoin"),
+		weechat::config_get("irc.server_default.autorejoin_delay")
+		);
+	return $message unless (
+		weechat::config_option_is_null($arj) ?
+		weechat::config_boolean($arjd) :
+		weechat::config_boolean($arj)
+		);
+
+	my ($nick, $channel, $reason) = ($msghash->{nick}, $msghash->{channel},
+		$msghash->{text});
+
+	my $buffer = ircbuf("$server.$channel");
+	my ($lserver, $lchannel) = (lc $server, lc $channel);
+
+	return $message unless mynick($buffer, $nick);
+	return $message unless exists $channels{$lserver}{$lchannel};
+	return $message if ignored($lserver);
+
+	weechat::print($buffer, weechat::prefix('quit').
+		weechat::color('irc.color.message_quit').
+		'You were forced to part '.weechat::color('chat_channel').$channel.
+		weechat::color('chat_delimiters').' ('.weechat::color('reset').
+		$reason.weechat::color('chat_delimiters').')'.
+		weechat::color('irc.color.message_quit').', rejoining');
+	my $delay = (
+		weechat::config_option_is_null($arj_delay) ?
+		weechat::config_integer($arjd_delay) :
+		weechat::config_integer($arj_delay)
+		);
+	weechat::command($buffer, ($delay ? "/wait $delay " : "").
+		"/join $channel");
+
+	return '';
+}
diff --git a/irc/weechat/banner.pl b/irc/weechat/banner.pl
@@ -0,0 +1,332 @@
+use strict;
+use warnings;
+no strict 'subs';
+
+my $SCRIPT_NAME = 'banner';
+my $SCRIPT_AUTHOR = 'The Krusty Krab <wowaname@volatile.ch>';
+my $SCRIPT_VERSION = '1.0';
+my $SCRIPT_LICENCE = 'Public domain';
+my $SCRIPT_DESC = 'Banner text';
+our (%queue, %timer);
+
+if (weechat::register($SCRIPT_NAME, $SCRIPT_AUTHOR, $SCRIPT_VERSION,
+ $SCRIPT_LICENCE, $SCRIPT_DESC, '', '')) {
+	weechat::hook_command('banner', 'Banner text',
+		"[-nick|-key|-limit] text",
+		"-nick: send to /nick command\n".
+		"-key: send as /mode +k (doesn't work on all ircds)\n".
+		"-limit: send as /mode +l\n",
+		'', 'cmd_banner', '');
+}
+
+sub cmd_banner
+{
+	my ($buffer, $cmd) = ($_[1], $_[2]);
+	my ($flag, $text) = $cmd =~ /^(-nick|-key|-limit|) *(.*)$/;
+	my @output;
+	my $prefix = '/msg *';
+	my $nick = weechat::info_get('irc_nick',
+		weechat::buffer_get_string($buffer, 'localvar_server'));
+
+my @chars = ('````````^',
+'XX``XXXXX',
+'``````XXX
+`````````
+``````XXX',
+'``X```X``
+XXXXXXXXX
+``X```X``
+XXXXXXXXX
+``X```X``',
+'`````XX``
+`X``X``X`
+XX``X``XX
+`X``X``X`
+``XX`````',
+'```X```XX
+XX``X``XX
+XX```X```',
+'```X`X```
+X`X`X`X`X
+``X```X``',
+'``````XXX',
+'`XXXXXXX`
+X```````X',
+'X```````X
+`XXXXXXX`',
+'``````X`X
+```````X`
+``````X`X',
+'````X````
+```XXX```
+````X````',
+'X````````
+`XX``````',
+'````X```^
+````X````',
+'X````````',
+'XXX``````
+```XXX```
+``````XXX',
+'`XXXXXXX`
+X```````X
+`XXXXXXX`',
+'X``````X`
+XXXXXXXXX
+X````````',
+'XXX````X`
+X``XX```X
+X````XXX`',
+'`X`````X`
+X```X```X
+`XXX`XXX`',
+'````XXX``
+````X``X`
+XXXXXXXXX',
+'X```XXXXX
+X```X```X
+`XXX````X',
+'`XXXXXXX`
+X```X```X
+`XXX`````',
+'XXX`````X
+```XXX``X
+``````XXX',
+'`XXX`XXX`
+X```X```X
+`XXX`XXX`',
+'`````XXX`
+X```X```X
+`XXXXXXX`',
+'``XX`XX``',
+'`X```````
+``XX`XX``',
+'````X````
+```X`X```
+``X```X``',
+'```X`X```
+```X`X``^
+```X`X```',
+'``X```X``
+```X`X```
+````X````',
+'```````X`
+XX``X```X
+`````XXX`',
+'`XXXXXXX`
+X``XXX``X
+X`X```X`X
+X`XXXXXXX',
+'XXXXXXXX`
+````X```X
+XXXXXXXX`',
+'XXXXXXXXX
+X```X```X
+`XXX`XXX`',
+'`XXXXXXX`
+X```````X
+`X`````X`',
+'XXXXXXXXX
+X```````X
+`XXXXXXX`',
+'XXXXXXXXX
+X```X```X
+X```````X',
+'XXXXXXXXX
+````X```X
+````````X',
+'`XXXXXXX`
+X```````X
+`XXX```X`',
+'XXXXXXXXX
+````X````
+XXXXXXXXX',
+'X```````X
+XXXXXXXXX
+X```````X',
+'`X``````X
+X```````X
+`XXXXXXXX',
+'XXXXXXXXX
+````X````
+```X`X```
+XXX```XXX',
+'XXXXXXXXX
+X````````',
+'XXXXXXXXX
+``````XX`
+``XXXX```
+``````XX`
+XXXXXXXXX',
+'XXXXXXXXX
+``````XX`
+```XXX```
+`XX``````
+XXXXXXXXX',
+'XXXXXXXXX
+X```````X
+XXXXXXXXX',
+'XXXXXXXXX
+````X```X
+`````XXX`',
+'`XXXXXXXX
+XX``````X
+XXXXXXXXX
+X````````',
+'XXXXXXXXX
+````X```X
+XXXX`XXX`',
+'`X```XXX`
+X```X```X
+`XXX```X`',
+'````````X
+XXXXXXXXX
+````````X',
+'XXXXXXXXX
+X````````
+XXXXXXXXX',
+'```XXXXXX
+XXX``````
+```XXXXXX',
+'`XXXXXXXX
+X````````
+`XXXX````
+X````````
+`XXXXXXXX',
+'XXX```XXX
+```XXX```
+XXX```XXX',
+'`````XXXX
+XXXXX````
+`````XXXX',
+'XXX`````X
+X``XXX``X
+X`````XXX',
+'XXXXXXXXX
+X```````X',
+'``````XXX
+```XXX```
+XXX``````',
+'X```````X
+XXXXXXXXX',
+'```````X`
+````````X
+```````X`',
+'X````````
+X```````^
+X````````',
+'````````X
+```````X`',
+'`X``X````
+X`X`X````
+XXXX`````',
+'XXXXXXXXX
+X```X````
+`XXX`````',
+'`XXX`````
+X```X````
+X```X````',
+'`XXX`````
+X```X````
+XXXXXXXXX',
+'`XXX`````
+X`X`X````
+X`XX`````',
+'XXXXXXXX`
+````X```X',
+'X``X`````
+X`X`X````
+`XXXX````',
+'XXXXXXXXX
+````X````
+XXXX`````',
+'XXXXX``X`',
+'X````````
+`XXXX``X`',
+'XXXXXXXXX
+````X````
+XXXX`X```',
+'X```````X
+XXXXXXXXX
+X````````',
+'XXXXX````
+````X````
+XXXX`````
+````X````
+XXXX`````',
+'XXXXX````
+````X````
+XXXX`````',
+'XXXXX````
+X```X````
+XXXXX````',
+'XXXXX````
+`X``X````
+``XX`````',
+'``XX`````
+`X``X````
+XXXXX````',
+'XXXXX````
+````X````',
+'X``X`````
+X`X`X````
+`X``X````',
+'`XXXXXXX`
+X```X````',
+'`XXXX````
+X````````
+XXXXX````',
+'``XXX````
+XX```````
+``XXX````',
+'`XXXX````
+X````````
+`XXX`````
+X````````
+`XXXX````',
+'XX`XX````
+``X``````
+XX`XX````',
+'X``XX````
+X`X``````
+`XXXX````',
+'XX``X````
+X`X`X````
+X``XX````',
+'````X````
+XXXX`XXXX
+X```````X',
+'XXXXXXXXX',
+'X```````X
+XXXX`XXXX
+````X````',
+' ```````X`
+````````X
+```````X`
+````````X');
+
+	for ($flag) {
+		/-nick/  and $prefix = '/nick', last;
+		/-key/   and $prefix = '/mode +k', last;
+		/-limit/ and $prefix = '/mode +l', last;
+	}
+
+	if ($flag eq '-limit') { $chars[$_] =~ y/`X/18/ for (0 .. (@chars - 1)) }
+
+	for my $char (split //, $text) {
+		push @output, $flag eq '-limit' ? '111111111' : '`````````';
+		push @output, split /\n/, $chars[ord($char) - 0x20];
+	}
+
+	weechat::command($buffer, "$prefix $_") for @output;
+
+	for ($flag) {
+		/-nick/  and weechat::command($buffer, "/nick $nick"), last;
+		/-key/   and weechat::command($buffer, "/mode +k `````````"),
+		             weechat::command($buffer, "/mode -k `````````"),
+		             last;
+		/-limit/ and weechat::command($buffer, "/mode -l"), last;
+	}
+
+	return weechat::WEECHAT_RC_OK;
+}
diff --git a/irc/weechat/colo.py b/irc/weechat/colo.py
@@ -0,0 +1,170 @@
+# -*- coding: utf-8 -*-
+#
+# Released into the Public Domain
+
+"""colo: make your chats noticable"""
+
+import random
+import re
+import weechat
+
+SCRIPT_NAME    = "colo"
+SCRIPT_AUTHOR  = "The Krusty Krab <wowaname@volatile.ch>"
+SCRIPT_VERSION = "2.2"
+SCRIPT_LICENSE = "Public domain"
+SCRIPT_DESC    = "Makes your chats noticable"
+
+# script options
+settings = {
+	"fmt": (
+		"%c13♥ %0%s%o %c13♥",
+		"Format string for text. %0 - %9 are different colours, %s is text, "
+		"%c %b %u %r %o are ^C ^B ^U ^R ^O respectively, and %% is a literal "
+		"percent sign. %0 is the primary colour that should be used with %s.",
+		),
+	"fgs": (
+		"04,05,06,13",
+		"Colour codes to cycle for the foreground. "
+		"Leave blank for no foreground colours.",
+		),
+	"bgs": (
+		"",
+		"Colour codes to cycle for the background. "
+		"Leave blank for no background colours.",
+		),
+	"ignore_buffers": (
+		"bitlbee.*,scripts",
+		"List of buffers to ignore. Glob matches unless "
+		"you prefix the name with 're:'.",
+		),
+	"whitelist_buffers": (
+		"",
+		"List of buffers to whitelist. Glob match unless "
+		"you prefix the name with 're:'. Useful with "
+		"ignore_buffers = \"*\"",
+		),
+	"whitelist_cmds": (
+		"me,amsg,say",
+		"Commands to colour.",
+		),
+	"profiles": (
+		"> greentext,! alert",
+		"List of prefix/profile pairs. If you type one of "
+		"these prefixes at the beginning of your message, "
+		"the options will switch to (profile)_pre, "
+		"(profile)_suf, (profile)_fgs, and (profile)_bgs. ",
+		),
+	"greentext_fmt": "%c3> %s",
+	"alert_fmt": "%c1,8/!\\%c8,1 %s %o%c1,8/!\\"
+}
+
+
+if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE,
+ SCRIPT_DESC, "", ""):
+	for opt, val in settings.iteritems():
+		setting, desc = val if type(val) == tuple else (val, "")
+		if desc: weechat.config_set_desc_plugin(opt, desc)
+		if weechat.config_is_set_plugin(opt): continue
+		weechat.config_set_plugin(opt, setting)
+
+	weechat.hook_modifier("input_text_for_buffer", "cb_colo", "")
+
+# prevent looping
+nest = False
+
+def glob_match (haystack, needle):
+	return re.search("^%s$" %
+	 re.escape(haystack).replace(r"\?", ".").replace(r"\*", ".*?"),
+	 needle)
+
+def is_command (string):
+	return (string.startswith("/") and not string.startswith("/ ") and
+	 string != "/" and string.split(" ", 1)[0].split("\n", 1)[0].find("/", 1)
+	 < 0)
+
+def cb_colo (data, mod, buf, input):
+	global nest
+	if nest:
+		nest = False
+	#	return input
+	buffer_name = weechat.buffer_get_string(buf, "name").lower()
+	output = ""
+	profile = ""
+
+	for pattern in weechat.config_get_plugin("whitelist_buffers").lower().split(","):
+		if (pattern.startswith("re:") and
+		 re.search(pattern[3:], buffer_name)) or glob_match(pattern, buffer_name):
+			break
+	else:
+		for pattern in weechat.config_get_plugin("ignore_buffers").lower().split(","):
+			if (pattern.startswith("re:") and
+			 re.search(pattern[3:], buffer_name)) or glob_match(pattern, buffer_name):
+				return input
+
+	if not input:
+		return input
+
+	if is_command(input):
+		for cmd in weechat.config_get_plugin("whitelist_cmds").lower().split(","):
+			if not input.startswith("/%s " % cmd): continue
+			output = "/%s " % cmd
+			input = input.split(" ", 1)[1] if " " in input else ""
+			break
+		else:
+			# XXX
+			return input.replace('\r','')
+
+	if input.startswith("//"): input = input[1:]
+
+	for profile_pairs in weechat.config_get_plugin("profiles").split(","):
+		prefix, name = profile_pairs.split()
+		if not input.startswith("%s " % prefix): continue
+		profile = "%s_" % name
+		input = input.split(" ",1)[1] if " " in input else ""
+		for opt in ("fmt", "fgs", "bgs"):
+			if weechat.config_is_set_plugin(profile + opt): continue
+			weechat.config_set_plugin(profile + opt, "")
+		break
+
+	fgs = weechat.config_get_plugin("%sfgs" % profile).split(",")
+	bgs = weechat.config_get_plugin("%sbgs" % profile).split(",")
+	fmt = weechat.config_get_plugin("%sfmt" % profile).split("%%")
+	
+	for i in xrange(len(fmt)):
+		fmt[i] = fmt[i].replace("%c", "\x03").replace("%b",
+		 "\x02").replace("%u", "\x1f").replace("%r",
+		 "\x16").replace("%o", "\x0f")
+		if fgs == [""] and bgs == [""]: continue
+		for j in xrange(10):
+			base = "\x03%s%s%s" % (
+				random.choice(fgs),
+				"," if bgs != [""] else "",
+				random.choice(bgs),
+				)
+			fmt[i] = fmt[i].replace("%%%d" % j, base)
+			if j: continue
+			input = re.sub(
+				"\x03([^0-9])",
+				"\x03%s\\1" % base,
+				input.replace("\x0f","\x0f%s" % base))
+
+	fmt = "%".join(fmt)
+	nest = is_command(fmt)
+	servername = weechat.buffer_get_string(buf, "localvar_server")
+	iptr = weechat.infolist_get("irc_server", "", servername)
+	weechat.infolist_next(iptr)
+	long_lines = weechat.infolist_integer(iptr, "cap_long_lines")
+	weechat.infolist_free(iptr)
+
+	nicklen = weechat.info_get("irc_server_isupport_value", "%s,NICKLEN" %
+	 servername)
+	if not nicklen: nicklen = 9
+
+	l = ((512 if long_lines else 0) + 409 - len(fmt) - int(nicklen))
+	o = []
+	for line in input.replace("\r", "\n").split("\n"):
+		if not line: continue
+		for i in xrange(0, len(line), l):
+			o.append(fmt.replace("%s", line[i:i+l].rstrip()))
+
+	return output + "\n".join(o)
diff --git a/irc/weechat/coloconv.pl b/irc/weechat/coloconv.pl
@@ -0,0 +1,45 @@
+#!/usr/bin/perl
+
+=pod
+
+Reads plugins.conf from stdin, writes new plugins.conf to stdout, and
+writes commands to restore the rest of the config options to stderr
+
+Suggested operation:
+cd .weechat
+./coloconv.pl < plugins.conf > plugins.conf.new 2> commands
+diff plugins.conf plugins.conf.new # to make sure nothing got clobbered
+
+Then in WeeChat:
+/exec -o .weechat/commands
+
+=cut
+
+my %profs;
+my $desc = 0;
+
+while (<>) {
+	$desc and print, next;
+	$_ !~ /^python\.embellish\./ and print, next;
+	s/^python\.embellish/python.colo/;
+	$_ !~ /^python\.colo\..*(?:pre|suf)/ and print, next;
+	$_ eq '[desc]' and ($desc = 1), print, next;
+
+	my ($prof, $k, $v) = /^python\.colo\.(.*)(pre|suf) = "(.*)"$/;
+	$v =~ s/\x02/%b/g;
+	$v =~ s/\x03/%c/g;
+	$v =~ s/\x0f/%o/g;
+	$v =~ s/\x16/%r/g;
+	$v =~ s/\x1f/%u/g;
+
+	if ($k eq 'pre') {
+		$profs{$prof} = "%0$v%o%0 %s%o%0 ";
+	} elsif ($k eq 'suf') {
+		$profs{$prof} .= $v;
+	}
+}
+
+for my $prof (keys %profs) {
+	print STDERR "/reload\n";
+	print STDERR "/set plugins.var.python.colo.${prof}fmt $profs{$prof}\n";
+}
diff --git a/irc/weechat/hueg.pl b/irc/weechat/hueg.pl
@@ -0,0 +1,1255 @@
+#!/usr/bin/perl
+
+
+######
+# hueg.pl PRO MODE
+# modded by ma0 and others
+# respekts 2 jakk and others
+######
+
+$maxchars = 10; #num of chars b4 split
+my $reverse = 0;
+my $flip = 0;
+my $mirror = 0;
+my $scale = 1;
+
+my $SCRIPT_NAME = 'hueg';
+my $SCRIPT_AUTHOR = 'LIFELIKE <i@wnt2die.com>';
+my $SCRIPT_VERSION = '1.1';
+my $SCRIPT_LICENCE = 'GPL3';
+my $SCRIPT_DESC = 'make text hueg LOL';
+
+if (weechat::register($SCRIPT_NAME, $SCRIPT_AUTHOR, $SCRIPT_VERSION,
+ $SCRIPT_LICENCE, $SCRIPT_DESC, '', '')) {
+	weechat::hook_command('hueg', '', '<string> [options]',
+		"      -rep <num>     num of times to scroll msg\n".
+		"      -re            reverses text\n".
+		"      -flip          flips text\n".
+		"      -mir           mirrors your text [NOT WORKIN LOL]\n".
+		"      -scale <num>   scales shit\n".
+		"      num,num,num    fg, shadow, bg colors (bg optional)",
+		'-rep|-re|-flip|-mir|-scale|%*', 'hueg', '');
+}
+
+sub hueg {
+	(undef, $buffer, $data) = @_;
+	$in = $data;
+
+	if ($in =~ /-rep (\d+)/i) {
+		$rep = $1;
+		$in =~ s/-rep \d+//i;
+	} else {
+		$rep = 1;
+	}
+
+	if($in =~ /-scale (\d+)/i) {
+		$scale = $1;
+		$in =~ s/-scale \d+//i;
+	} else {
+		$scale = 1;
+	}
+
+	if($in =~ /-re/i) {
+		$reverse = 1;
+		$in =~ s/-re//i;
+	} else {
+		$reverse = 0;
+	}
+
+	if($in =~ /-flip/i){
+		$flip = 1;
+		$in =~ s/-flip//i;
+	} else {
+		$flip = 0;
+	}
+
+	if($in =~ /-mir/i) {
+		$mirror = 1;
+		$in =~ s/-mir//i;
+	} else {
+		$mirror = 0;
+	}
+
+	$in =~ s/\s+$//;
+
+	if ($in eq '') {
+		weechat::print($buffer, weechat::prefix('error').
+			"Invalid syntax; see /help hueg");
+		return weechat::WEECHAT_RC_ERROR;
+	} else {
+		until ($rep == 0) {
+			colors();
+			parse();
+			process();
+			select(undef,undef,undef,.1);
+			$rep--;
+		}
+		return weechat::WEECHAT_RC_OK;
+	}
+}
+
+sub colors {
+	if ($data =~ /(\d+),(\d+),(\d+)/) {
+		$c2 = "\cC$1,$1";   #fg
+		$c1 = "\cC$2,$2";   #sh
+		$c3 = "\cC$3,$3";   #bg
+		$in =~ s/\d+,\d+,\d+//;
+	} elsif ($data =~ /(\d+),(\d+)/) {
+		$c2 = "\cC$1,$1";   #fg
+		$c1 = "\cC$2,$2";   #sh
+		$c3 = "\cO";        #bg (trans)
+		$in =~ s/\d+,\d+//;
+	} else {
+		$r1 = $r2 = 0;
+		until ($r1 > 1) { $r1 = int rand(15); }
+		until ($r2 > 1 && $r2 != $r1) { $r2 = int rand(15); }
+		$c2 = "\cC$r1,$r1"; #fg (rand)
+		$c1 = "\cC$r2,$r2"; #sh (rand)
+		$c3 = "\cO";        #bg (trans)
+	}
+	db1();
+}
+
+sub parse {
+	$in =~ s/(\S{$maxchars})/$1 /g;
+	undef @s0;
+	@s0 = split(' ',$in);
+	undef @s1;
+	$s1n = 0;
+	for $n (@s0) {
+		$nlen = length($n);
+		$slen = length($s1[$s1n] // '') + $nlen;
+		if ($slen <= $maxchars) {
+			$s1[$s1n] .= "$n ";
+		} else {
+			$s1n++;
+			$s1[$s1n] .= "$n ";
+		}
+	}
+}
+
+sub process {
+	for $n (@s1) { #each line
+		if ($reverse) {
+			$n = reverse $n;
+		}
+
+		$n =~ s/\s$//;
+		$n =~ s/^\s//;
+		undef @s2;
+		@s2 = split('',$n);
+		my $cur; # current string
+		my $tmp;
+
+		for $f (0..8*$scale) {
+			for $l (@s2) { #each letter
+				$all .= "$c3 \cO";
+
+				if($flip) { $cur = $db{$l}[(9-$f)/$scale] // $db{'?'}[(9-$f)/$scale]; } #line of letter
+				else { $cur = $db{$l}[$f/$scale] // $db{'?'}[$f/$scale]; }
+
+				$whitespace = " " x $scale;
+				$cur =~ s/ /$whitespace/g;
+				$whitespace = "#" x $scale;
+				$cur =~ s/#/$whitespace/g;
+				$whitespace = "." x $scale;
+				$cur =~ s/\./$whitespace/g;
+				$all .= $cur;
+			}
+
+			$all .= "$c3 ";
+			if($mirror) { $all = reverse $all; }
+			weechat::command($buffer, "/msg * $all");
+			$all = '';
+		}
+
+	}
+}
+
+#------------------#
+#   character db   #
+#       lol        #
+#------------------#
+
+sub db1 {
+%db = (
+" " => [
+"$c3      ",
+"$c3      ",
+"$c3      ",
+"$c3      ",
+"$c3      ",
+"$c3      ",
+"$c3      ",
+"$c3      ",
+"$c3      ",
+],
+"\cC" => [
+"$c3             ",
+"$c1.$c2##$c3  $c1.$c2#######$c3",
+"$c1.$c2##$c3  $c1.$c2##$c3     ",
+"$c1.$c2##$c3  $c1.$c2##$c3     ",
+"$c1.$c2############$c3",
+"$c3     $c1.$c2##$c3  $c1.$c2##$c3",
+"$c3     $c1.$c2##$c3  $c1.$c2##$c3",
+"$c1.$c2#######$c3  $c1.$c2##$c3",
+"$c3             ",
+],
+"\cB" => [
+"$c3             ",
+"$c1.$c2##$c3  $c1.$c2#######$c3",
+"$c1.$c2##$c3  $c1.$c2##$c3     ",
+"$c1.$c2##$c3  $c1.$c2##$c3     ",
+"$c1.$c2############$c3",
+"$c3     $c1.$c2##$c3  $c1.$c2##$c3",
+"$c3     $c1.$c2##$c3  $c1.$c2##$c3",
+"$c1.$c2#######$c3  $c1.$c2##$c3",
+"$c3             ",
+],
+"\cO" => [
+"$c3             ",
+"$c1.$c2##$c3  $c1.$c2#######$c3",
+"$c1.$c2##$c3  $c1.$c2##$c3     ",
+"$c1.$c2##$c3  $c1.$c2##$c3     ",
+"$c1.$c2############$c3",
+"$c3     $c1.$c2##$c3  $c1.$c2##$c3",
+"$c3     $c1.$c2##$c3  $c1.$c2##$c3",
+"$c1.$c2#######$c3  $c1.$c2##$c3",
+"$c3             ",
+],
+"0" => [
+"$c3         ",
+"$c3 $c1.$c2######$c3 ",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c3 $c1.$c2######$c3 ",
+"$c3         ",
+],
+"1" => [
+"$c3       ",
+"$c3  $c1.$c2##$c3  ",
+"$c3 $c1.$c2###$c3  ",
+"$c1.$c2####$c3  ",
+"$c3  $c1.$c2##$c3  ",
+"$c3  $c1.$c2##$c3  ",
+"$c3  $c1.$c2##$c3  ",
+"$c1.$c2######$c3",
+"$c3       ",
+],
+"2" => [
+"$c3        ",
+"$c3 $c1.$c2#####$c3 ",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c3    $c1.$c2##$c3 ",
+"$c3   $c1.$c2##$c3  ",
+"$c3  $c1.$c2##$c3   ",
+"$c3 $c1.$c2##$c3    ",
+"$c1.$c2#######$c3",
+"$c3        ",
+],
+"3" => [
+"$c3        ",
+"$c3 $c1.$c2#####$c3 ",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c3     $c1.$c2##$c3",
+"$c3   $c1.$c2###$c3 ",
+"$c3     $c1.$c2##$c3",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c3 $c1.$c2#####$c3 ",
+"$c3        ",
+],
+"4" => [
+"$c3        ",
+"$c3    $c1.$c2##$c3 ",
+"$c3   $c1.$c2###$c3 ",
+"$c3  $c1.$c2#$c1.$c2##$c3 ",
+"$c3 $c1.$c2#$c3 $c1.$c2##$c3 ",
+"$c1.$c2#######$c3",
+"$c3    $c1.$c2##$c3 ",
+"$c3    $c1.$c2##$c3 ",
+"$c3        ",
+],
+"5" => [
+"$c3        ",
+"$c1.$c2######$c3 ",
+"$c1.$c2##$c3     ",
+"$c1.$c2##$c3     ",
+"$c1.$c2######$c3 ",
+"$c3     $c1.$c2##$c3",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c3 $c1.$c2#####$c3 ",
+"$c3        ",
+],
+"6" => [
+"$c3        ",
+"$c3   $c1.$c2##$c3  ",
+"$c3  $c1.$c2##$c3   ",
+"$c3 $c1.$c2##$c3    ",
+"$c1.$c2######$c3 ",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c3 $c1.$c2#####$c3 ",
+"$c3        ",
+],
+"7" => [
+"$c3         ",
+"$c1.$c2########$c3",
+"$c3      $c1.$c2##$c3",
+"$c3     $c1.$c2##$c3 ",
+"$c3    $c1.$c2##$c3  ",
+"$c3   $c1.$c2##$c3   ",
+"$c3  $c1.$c2##$c3    ",
+"$c3 $c1.$c2##$c3     ",
+"$c3         ",
+],
+"8" => [
+"$c3         ",
+"$c3 $c1.$c2######$c3 ",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c3 $c1.$c2######$c3 ",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c3 $c1.$c2######$c3 ",
+"$c3         ",
+],
+"9" => [
+"$c3        ",
+"$c3 $c1.$c2#####$c3 ",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c3 $c1.$c2######$c3",
+"$c3    $c1.$c2##$c3 ",
+"$c3   $c1.$c2##$c3  ",
+"$c3  $c1.$c2##$c3   ",
+"$c3        ",
+],
+A => [
+"$c3        ",
+"$c3  $c1.$c2###$c3  ",
+"$c3 $c1.$c2##$c1.$c2##$c3 ",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c1.$c2#######$c3",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c3        ",
+],
+a => [
+"$c3         ",
+"$c3         ",
+"$c3         ",
+"$c3 $c1.$c2#####$c3  ",
+"$c3     $c1.$c2##$c3 ",
+"$c3 $c1.$c2######$c3 ",
+"$c1.$c2##$c3  $c1.$c2##$c3 ",
+"$c3 $c1.$c2#######$c3",
+"$c3         ",
+],
+B => [
+"$c3         ",
+"$c1.$c2#######$c3 ",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2#######$c3 ",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2#######$c3 ",
+"$c3         ",
+],
+b => [
+"$c3         ",
+"$c1.$c2##$c3      ",
+"$c1.$c2##$c3      ",
+"$c1.$c2######$c3  ",
+"$c1.$c2##$c3  $c1.$c2##$c3 ",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3  $c1.$c2##$c3 ",
+"$c1.$c2######$c3  ",
+"$c3         ",
+],
+C => [
+"$c3         ",
+"$c3 $c1.$c2######$c3 ",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3      ",
+"$c1.$c2##$c3      ",
+"$c1.$c2##$c3      ",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c3 $c1.$c2######$c3 ",
+"$c3         ",
+],
+c => [
+"$c3        ",
+"$c3        ",
+"$c3        ",
+"$c3  $c1.$c2#####$c3",
+"$c3 $c1.$c2##$c3    ",
+"$c1.$c2##$c3     ",
+"$c3 $c1.$c2##$c3    ",
+"$c3  $c1.$c2#####$c3",
+"$c3        ",
+],
+D => [
+"$c3          ",
+"$c1.$c2#######$c3  ",
+"$c1.$c2##$c3   $c1.$c2##$c3 ",
+"$c1.$c2##$c3    $c1.$c2##$c3",
+"$c1.$c2##$c3    $c1.$c2##$c3",
+"$c1.$c2##$c3    $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3 ",
+"$c1.$c2#######$c3  ",
+"$c3          ",
+],
+d => [
+"$c3         ",
+"$c3      $c1.$c2##$c3",
+"$c3      $c1.$c2##$c3",
+"$c3  $c1.$c2######$c3",
+"$c3 $c1.$c2##$c3  $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c3 $c1.$c2##$c3  $c1.$c2##$c3",
+"$c3  $c1.$c2######$c3",
+"$c3         ",
+],
+E => [
+"$c3        ",
+"$c1.$c2#######$c3",
+"$c1.$c2##$c3     ",
+"$c1.$c2##$c3     ",
+"$c1.$c2######$c3 ",
+"$c1.$c2##$c3     ",
+"$c1.$c2##$c3     ",
+"$c1.$c2#######$c3",
+"$c3        ",
+],
+e => [
+"$c3         ",
+"$c3         ",
+"$c3         ",
+"$c3 $c1.$c2######$c3 ",
+"$c1.$c2##$c3   $c1.$c2##",
+"$c1.$c2#######$c3 ",
+"$c1.$c2##$c3      ",
+"$c3 $c1.$c2######$c3 ",
+"$c3         ",
+],
+F => [
+"$c3        ",
+"$c1.$c2#######",
+"$c1.$c2##$c3     ",
+"$c1.$c2##$c3     ",
+"$c1.$c2######$c3 ",
+"$c1.$c2##$c3     ",
+"$c1.$c2##$c3     ",
+"$c1.$c2##$c3     ",
+"$c3        ",
+],
+f => [
+"$c3      ",
+"$c3      ",
+"$c3  $c1.$c2###",
+"$c3 $c1.$c2##$c3  ",
+"$c1.$c2#####",
+"$c3 $c1.$c2##$c3  ",
+"$c3 $c1.$c2##$c3  ",
+"$c3 $c1.$c2##$c3  ",
+"$c3      ",
+],
+G => [
+"$c3          ",
+"$c3  $c1.$c2######$c3 ",
+"$c3 $c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3       ",
+"$c1.$c2##$c3  $c1.$c2####$c3",
+"$c1.$c2##$c3    $c1.$c2##$c3",
+"$c3 $c1.$c2##$c3   $c1.$c2##$c3",
+"$c3  $c1.$c2######$c3 ",
+"$c3          ",
+],
+g => [
+"$c3         ",
+"$c3         ",
+"$c3         ",
+"$c3 $c1.$c2######$c3 ",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c3 $c1.$c2#######$c3",
+"$c3      $c1.$c2##$c3",
+"$c3 $c1.$c2######$c3 ",
+],
+H => [
+"$c3         ",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2########$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c3         ",
+],
+h => [
+"$c3         ",
+"$c1.$c2##$c3      ",
+"$c1.$c2##$c3      ",
+"$c1.$c2#######$c3 ",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c3         ",
+],
+I => [
+"$c3       ",
+"$c1.$c2######$c3",
+"$c3  $c1.$c2##$c3  ",
+"$c3  $c1.$c2##$c3  ",
+"$c3  $c1.$c2##$c3  ",
+"$c3  $c1.$c2##$c3  ",
+"$c3  $c1.$c2##$c3  ",
+"$c1.$c2######$c3",
+"$c3       ",
+],
+i => [
+"$c3     ",
+"$c3 $c1.$c2##$c3 ",
+"$c3     ",
+"$c1.$c2###$c3 ",
+"$c3 $c1.$c2##$c3 ",
+"$c3 $c1.$c2##$c3 ",
+"$c3 $c1.$c2##$c3 ",
+"$c1.$c2####",
+"$c3     ",
+],
+J => [
+"$c3        ",
+"$c3 $c1.$c2######",
+"$c3    $c1.$c2##$c3 ",
+"$c3    $c1.$c2##$c3 ",
+"$c3    $c1.$c2##$c3 ",
+"$c3    $c1.$c2##$c3 ",
+"$c1.$c2##$c3 $c1.$c2##$c3 ",
+"$c3 $c1.$c2####$c3  ",
+"$c3        ",
+],
+j => [
+"$c3       ",
+"$c3    $c1.$c2##",
+"$c3       ",
+"$c3   $c1.$c2###",
+"$c3    $c1.$c2##",
+"$c3    $c1.$c2##",
+"$c3    $c1.$c2##",
+"$c1.$c2##$c3 $c1.$c2##",
+"$c3 $c1.$c2####$c3 ",
+],
+K => [
+"$c3        ",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c1.$c2##$c3 $c1.$c2##$c3 ",
+"$c1.$c2##$c1.$c2##$c3  ",
+"$c1.$c2####$c3   ",
+"$c1.$c2##$c1.$c2##$c3  ",
+"$c1.$c2##$c3 $c1.$c2##$c3 ",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c3        ",
+],
+k => [
+"$c3        ",
+"$c1.$c2##$c3     ",
+"$c1.$c2##$c3     ",
+"$c1.$c2##$c3  $c1.$c2##",
+"$c1.$c2##$c3 $c1.$c2##$c3 ",
+"$c1.$c2#####$c3  ",
+"$c1.$c2##$c3 $c1.$c2##$c3 ",
+"$c1.$c2##$c3  $c1.$c2##",
+"$c3        ",
+],
+L => [
+"$c3        ",
+"$c1.$c2##$c3     ",
+"$c1.$c2##$c3     ",
+"$c1.$c2##$c3     ",
+"$c1.$c2##$c3     ",
+"$c1.$c2##$c3     ",
+"$c1.$c2##$c3     ",
+"$c1.$c2#######$c3",
+"$c3        ",
+],
+l => [
+"$c3     ",
+"$c1.$c2###$c3 ",
+"$c3 $c1.$c2##$c3 ",
+"$c3 $c1.$c2##$c3 ",
+"$c3 $c1.$c2##$c3 ",
+"$c3 $c1.$c2##$c3 ",
+"$c3 $c1.$c2##$c3 ",
+"$c1.$c2####",
+"$c3     ",
+],
+M => [
+"$c3            ",
+"$c1.$c2##$c3      $c1.$c2##$c3",
+"$c1.$c2###$c3    $c1.$c2###$c3",
+"$c1.$c2####$c3  $c1.$c2####$c3",
+"$c1.$c2##$c1.$c2##$c1.$c2##$c1.$c2##$c3",
+"$c1.$c2##$c3 $c1.$c2###$c3 $c1.$c2##$c3",
+"$c1.$c2##$c3  $c1.$c2#$c3  $c1.$c2##$c3",
+"$c1.$c2##$c3      $c1.$c2##$c3",
+"$c3            ",
+],
+m => [
+"$c3          ",
+"$c3          ",
+"$c3          ",
+"$c3 $c1.$c2##$c3  $c1.$c2##$c3 ",
+"$c1.$c2####$c1.$c2####$c3",
+"$c1.$c2##$c1.$c2###$c1.$c2##$c3",
+"$c1.$c2##$c3 $c1.$c2#$c3 $c1.$c2##$c3",
+"$c1.$c2##$c3    $c1.$c2##$c3",
+"$c3          ",
+],
+N => [
+"$c3           ",
+"$c1.$c2###$c3    $c1.$c2##",
+"$c1.$c2####$c3   $c1.$c2##",
+"$c1.$c2##$c1.$c2##$c3  $c1.$c2##",
+"$c1.$c2##$c3 $c1.$c2##$c3 $c1.$c2##",
+"$c1.$c2##$c3  $c1.$c2##$c1.$c2##",
+"$c1.$c2##$c3   $c1.$c2####",
+"$c1.$c2##$c3    $c1.$c2###",
+"$c3           ",
+],
+n => [
+"$c3         ",
+"$c3         ",
+"$c3         ",
+"$c3 $c1.$c2######$c3 ",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c3         ",
+],
+O => [
+"$c3           ",
+"$c3  $c1.$c2######$c3  ",
+"$c3 $c1.$c2##$c3   $c1.$c2##$c3 ",
+"$c1.$c2##$c3     $c1.$c2##$c3",
+"$c1.$c2##$c3     $c1.$c2##$c3",
+"$c1.$c2##$c3     $c1.$c2##$c3",
+"$c3 $c1.$c2##$c3   $c1.$c2##$c3 ",
+"$c3  $c1.$c2######$c3  ",
+"$c3           ",
+],
+o => [
+"$c3         ",
+"$c3         ",
+"$c3         ",
+"$c3 $c1.$c2######$c3 ",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c3 $c1.$c2######$c3 ",
+"$c3         ",
+],
+P => [
+"$c3         ",
+"$c1.$c2#######$c3 ",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2#######$c3 ",
+"$c1.$c2##$c3      ",
+"$c1.$c2##$c3      ",
+"$c1.$c2##$c3      ",
+"$c3         ",
+],
+p => [
+"$c3         ",
+"$c3         ",
+"$c3         ",
+"$c3 $c1.$c2######$c3 ",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2#######$c3 ",
+"$c1.$c2##$c3      ",
+"$c1.$c2##$c3      ",
+],
+Q => [
+"$c3           ",
+"$c3  $c1.$c2######$c3  ",
+"$c3 $c1.$c2##$c3   $c1.$c2##$c3 ",
+"$c1.$c2##$c3     $c1.$c2##$c3",
+"$c1.$c2##$c3     $c1.$c2##$c3",
+"$c1.$c2##$c3  $c1.$c2##$c1.$c2##$c3",
+"$c3 $c1.$c2##$c3  $c1.$c2###$c3 ",
+"$c3  $c1.$c2######$c3  ",
+"$c3       $c1.$c2##$c3 ",
+],
+q => [
+"$c3         ",
+"$c3         ",
+"$c3         ",
+"$c3 $c1.$c2######$c3 ",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c3 $c1.$c2#######$c3",
+"$c3      $c1.$c2##$c3",
+"$c3      $c1.$c2##$c3",
+],
+R => [
+"$c3           ",
+"$c1.$c2#######$c3   ",
+"$c1.$c2##$c3   $c1.$c2##$c3  ",
+"$c1.$c2##$c3   $c1.$c2##$c3  ",
+"$c1.$c2#######$c3   ",
+"$c1.$c2##$c3   $c1.$c2##$c3  ",
+"$c1.$c2##$c3    $c1.$c2##$c3 ",
+"$c1.$c2##$c3     $c1.$c2##",
+"$c3           ",
+],
+r => [
+"$c3        ",
+"$c3        ",
+"$c3        ",
+"$c3 $c1.$c2#####$c3 ",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c1.$c2##$c3     ",
+"$c1.$c2##$c3     ",
+"$c1.$c2##$c3     ",
+"$c3        ",
+],
+S => [
+"$c3        ",
+"$c3 $c1.$c2#####$c3 ",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c1.$c2##$c3     ",
+"$c3 $c1.$c2#####$c3 ",
+"$c3     $c1.$c2##$c3",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c3 $c1.$c2#####$c3 ",
+"$c3        ",
+],
+s => [
+"$c3       ",
+"$c3       ",
+"$c3       ",
+"$c3 $c1.$c2#####$c3",
+"$c1.$c2##$c3    ",
+"$c3 $c1.$c2####$c3 ",
+"$c3    $c1.$c2##$c3",
+"$c1.$c2#####$c3 ",
+"$c3       ",
+],
+T => [
+"$c3         ",
+"$c1.$c2########$c3",
+"$c3   $c1.$c2##$c3   ",
+"$c3   $c1.$c2##$c3   ",
+"$c3   $c1.$c2##$c3   ",
+"$c3   $c1.$c2##$c3   ",
+"$c3   $c1.$c2##$c3   ",
+"$c3   $c1.$c2##$c3   ",
+"$c3         ",
+],
+t => [
+"$c3       ",
+"$c3       ",
+"$c3 $c1.$c2##$c3   ",
+"$c1.$c2#####$c3 ",
+"$c3 $c1.$c2##$c3   ",
+"$c3 $c1.$c2##$c3   ",
+"$c3 $c1.$c2##$c1.$c2##",
+"$c3  $c1.$c2###$c3 ",
+"$c3       ",
+],
+U => [
+"$c3           ",
+"$c1.$c2##$c3     $c1.$c2##$c3",
+"$c1.$c2##$c3     $c1.$c2##$c3",
+"$c1.$c2##$c3     $c1.$c2##$c3",
+"$c1.$c2##$c3     $c1.$c2##$c3",
+"$c1.$c2##$c3     $c1.$c2##$c3",
+"$c3 $c1.$c2##$c3   $c1.$c2##$c3 ",
+"$c3  $c1.$c2######$c3  ",
+"$c3           ",
+],
+u => [
+"$c3         ",
+"$c3         ",
+"$c3         ",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c1.$c2##$c3   $c1.$c2##$c3",
+"$c3 $c1.$c2######$c3 ",
+"$c3         ",
+],
+V => [
+"$c3             ",
+"$c1.$c2#$c3         $c1.$c2#",
+"$c1.$c2##$c3       $c1.$c2##",
+"$c3 $c1.$c2##$c3     $c1.$c2##$c3 ",
+"$c3  $c1.$c2##$c3   $c1.$c2##$c3  ",
+"$c3   $c1.$c2##$c3 $c1.$c2##$c3   ",
+"$c3    $c1.$c2####$c3    ",
+"$c3     $c1.$c2##$c3     ",
+"$c3             ",
+],
+v => [
+"$c3            ",
+"$c3            ",
+"$c3            ",
+"$c1.$c2##$c3      $c1.$c2##",
+"$c3 $c1.$c2##$c3    $c1.$c2##$c3 ",
+"$c3  $c1.$c2##$c3  $c1.$c2##$c3  ",
+"$c3   $c1.$c2##$c1.$c2##$c3   ",
+"$c3    $c1.$c2###$c3    ",
+"$c3            ",
+],
+W => [
+"$c3           ",
+"$c1.$c2##$c3     $c1.$c2##",
+"$c1.$c2##$c3     $c1.$c2##",
+"$c1.$c2##$c3     $c1.$c2##",
+"$c1.$c2##$c3 $c1.$c2##$c3 $c1.$c2##",
+"$c1.$c2##$c1.$c2####$c1.$c2##",
+"$c1.$c2####$c3 $c1.$c2####",
+"$c3 $c1.$c2##$c3   $c1.$c2##$c3 ",
+"$c3           ",
+],
+w => [
+"$c3          ",
+"$c3          ",
+"$c3          ",
+"$c1.$c2##$c3    $c1.$c2##$c3",
+"$c1.$c2##$c3 $c1.$c2#$c3 $c1.$c2##$c3",
+"$c1.$c2##$c1.$c2###$c1.$c2##$c3",
+"$c1.$c2####$c1.$c2####$c3",
+"$c3 $c1.$c2##$c3  $c1.$c2##$c3 ",
+"$c3          ",
+],
+X => [
+"$c3          ",
+"$c1.$c2##$c3    $c1.$c2##$c3",
+"$c3 $c1.$c2##$c3  $c1.$c2##$c3 ",
+"$c3  $c1.$c2##$c1.$c2##$c3  ",
+"$c3   $c1.$c2###$c3   ",
+"$c3  $c1.$c2##$c1.$c2##$c3  ",
+"$c3 $c1.$c2##$c3  $c1.$c2##$c3 ",
+"$c1.$c2##$c3    $c1.$c2##$c3",
+"$c3          ",
+],
+x => [
+"$c3        ",
+"$c3        ",
+"$c3        ",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c3 $c1.$c2##$c1.$c2##$c3 ",
+"$c3   $c2###$c3  ",
+"$c3 $c1.$c2##$c1.$c2##$c3 ",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c3        ",
+],
+Y => [
+"$c3           ",
+"$c1.$c2##$c3     $c1.$c2##$c3",
+"$c3 $c1.$c2##$c3   $c1.$c2##$c3 ",
+"$c3  $c1.$c2##$c3 $c1.$c2##$c3  ",
+"$c3   $c1.$c2####$c3   ",
+"$c3    $c1.$c2##$c3    ",
+"$c3    $c1.$c2##$c3    ",
+"$c3    $c1.$c2##$c3    ",
+"$c3           ",
+],
+y => [
+"$c3           ",
+"$c3           ",
+"$c3           ",
+"$c1.$c2##$c3     $c1.$c2##$c3",
+"$c3 $c1.$c2##$c3   $c1.$c2##$c3 ",
+"$c3  $c1.$c2##$c3 $c1.$c2##$c3  ",
+"$c3   $c1.$c2####$c3   ",
+"$c3    $c1.$c2##$c3    ",
+"$c3   $c1.$c2##$c3     ",
+],
+Z => [
+"$c3         ",
+"$c1.$c2########$c3",
+"$c3     $c1.$c2##$c3 ",
+"$c3    $c1.$c2##$c3  ",
+"$c3   $c1.$c2##$c3   ",
+"$c3  $c1.$c2##$c3    ",
+"$c3 $c1.$c2##$c3     ",
+"$c1.$c2########$c3",
+"$c3         ",
+],
+z => [
+"$c3          ",
+"$c3          ",
+"$c3          ",
+"$c1.$c2########$c3 ",
+"$c3     $c1.$c2##$c3  ",
+"$c3   $c1.$c2##$c3    ",
+"$c3 $c1.$c2##$c3      ",
+"$c1.$c2########$c3 ",
+"$c3          ",
+],
+'~' => [
+"$c3             ",
+"$c3             ",
+"$c3             ",
+"$c3  $c1.$c2####$c3   $c1.$c2##$c3",
+"$c3 $c1.$c2##$c3 $c1.$c2##$c3 $c1.$c2##$c3 ",
+"$c1.$c2##$c3   $c1.$c2####$c3  ",
+"$c3             ",
+"$c3             ",
+"$c3             ",
+],
+'`' => [
+"$c3    ",
+"$c1.$c2##$c3 ",
+"$c3 $c1.$c2##",
+"$c3    ",
+"$c3    ",
+"$c3    ",
+"$c3    ",
+"$c3    ",
+"$c3    ",
+],
+'!' => [
+"$c3         ",
+"$c3      $c1.$c2##$c3",
+"$c3     $c1.$c2##$c3 ",
+"$c3    $c1.$c2##$c3  ",
+"$c3   $c1.$c2##$c3   ",
+"$c3  $c1.$c2##$c3    ",
+"$c3         ",
+"$c1.$c2##$c3      ",
+"$c3         ",
+],
+'@' => [
+"$c3            ",
+"$c3  $c1.$c2#######$c3  ",
+"$c3 $c1.$c2##$c3    $c1.$c2##$c3 ",
+"$c1.$c2##$c3  $c1.$c2##$c3 $c1.$c2##$c3",
+"$c1.$c2##$c3 $c1.$c2##$c3  $c1.$c2##$c3",
+"$c1.$c2##$c3  $c1.$c2#####$c3 ",
+"$c3 $c1.$c2##$c3        ",
+"$c3  $c1.$c2#######$c3  ",
+"$c3            ",
+],
+'#' => [
+"$c3           ",
+"$c3  $c1.$c2##$c3 $c1.$c2##$c3  ",
+"$c3  $c1.$c2##$c3 $c1.$c2##$c3  ",
+"$c1.$c2##########",
+"$c3  $c1.$c2##$c3 $c1.$c2##$c3  ",
+"$c1.$c2##########",
+"$c3  $c1.$c2##$c3 $c1.$c2##$c3  ",
+"$c3  $c1.$c2##$c3 $c1.$c2##$c3  ",
+"$c3           ",
+],
+'$' => [
+"$c3    $c1.$c2#$c3    ",
+"$c3 $c1.$c2#######$c3 ",
+"$c1.$c2##$c3 $c1.$c2#$c3 $c1.$c2##$c3",
+"$c1.$c2##$c3 $c1.$c2#$c3    ",
+"$c3 $c1.$c2#######$c3 ",
+"$c3    $c1.$c2#$c3 $c1.$c2##$c3",
+"$c1.$c2##$c3 $c1.$c2#$c3 $c1.$c2##$c3",
+"$c3 $c1.$c2#######$c3 ",
+"$c3    $c1.$c2#$c3    ",
+],
+'%' => [
+"$c3         ",
+"$c1.$c2##$c3   $c1.$c2##",
+"$c3     $c1.$c2##$c3 ",
+"$c3    $c1.$c2##$c3  ",
+"$c3   $c1.$c2##$c3   ",
+"$c3  $c1.$c2##$c3    ",
+"$c3 $c1.$c2##$c3     ",
+"$c1.$c2##$c3   $c1.$c2##",
+"$c3         ",
+],
+'^' => [
+"$c3        ",
+"$c3        ",
+"$c3  $c1.$c2###$c3  ",
+"$c3 $c1.$c2##$c1.$c2##$c3 ",
+"$c1.$c2##$c3  $c1.$c2##$c3",
+"$c3        ",
+"$c3        ",
+"$c3        ",
+"$c3        ",
+],
+'&' => [
+"$c3           ",
+"$c3  $c1.$c2####$c3    ",
+"$c3 $c1.$c2##$c3 $c1.$c2##$c3   ",
+"$c3  $c1.$c2####$c3    ",
+"$c3 $c1.$c2##$c3 $c1.$c2##$c3   ",
+"$c1.$c2##$c3   $c1.$c2##$c3  ",
+"$c1.$c2##$c3    $c1.$c2##$c3 ",
+"$c3 $c1.$c2######$c1.$c2##$c3",
+"$c3           ",
+],
+'*' => [
+"$c3     ",
+"$c3     ",
+"$c1.$c2#$c3 $c1.$c2#",
+"$c3 $c1.$c2##$c3 ",
+"$c1.$c2#$c3 $c1.$c2#",
+"$c3     ",
+"$c3     ",
+"$c3     ",
+"$c3     ",
+],
+'(' => [
+"$c3     ",
+"$c3  $c1.$c2##$c3",
+"$c3 $c1.$c2##$c3 ",
+"$c1.$c2##$c3  ",
+"$c1.$c2##$c3  ",
+"$c1.$c2##$c3  ",
+"$c3 $c1.$c2##$c3 ",
+"$c3  $c1.$c2##$c3",
+"$c3     ",
+],
+')' => [
+"$c3     ",
+"$c1.$c2##$c3  ",
+"$c3 $c1.$c2##$c3 ",
+"$c3  $c1.$c2##$c3",
+"$c3  $c1.$c2##$c3",
+"$c3  $c1.$c2##$c3",
+"$c3 $c1.$c2##$c3 ",
+"$c1.$c2##$c3  ",
+"$c3     ",
+],
+'_' => [
+"$c3         ",
+"$c3         ",
+"$c3         ",
+"$c3         ",
+"$c3         ",
+"$c3         ",
+"$c3         ",
+"$c1.$c2########$c3",
+"$c3         ",
+],
+'-' => [
+"$c3         ",
+"$c3         ",
+"$c3         ",
+"$c3         ",
+"$c1.$c2########$c3",
+"$c3         ",
+"$c3         ",
+"$c3         ",
+"$c3         ",
+],
+'+' => [
+"$c3         ",
+"$c3         ",
+"$c3   $c1.$c2##$c3   ",
+"$c3   $c1.$c2##$c3   ",
+"$c1.$c2########$c3",
+"$c3   $c1.$c2##$c3   ",
+"$c3   $c1.$c2##$c3   ",
+"$c3         ",
+"$c3         ",
+],
+'=' => [
+"$c3         ",
+"$c3         ",
+"$c3         ",
+"$c1.$c2########$c3",
+"$c3         ",
+"$c1.$c2########$c3",
+"$c3         ",
+"$c3         ",
+"$c3         ",
+],
+'|' => [
+"$c3   ",
+"$c1.$c2##$c3",
+"$c1.$c2##$c3",
+"$c1.$c2##$c3",
+"$c1.$c2##$c3",
+"$c1.$c2##$c3",
+"$c1.$c2##$c3",
+"$c1.$c2##$c3",
+"$c3   ",
+],
+'\\' => [
+"$c3         ",
+"$c1.$c2##$c3      ",
+"$c3 $c1.$c2##$c3     ",
+"$c3  $c1.$c2##$c3    ",
+"$c3   $c1.$c2##$c3   ",
+"$c3    $c1.$c2##$c3  ",
+"$c3     $c1.$c2##$c3 ",
+"$c3      $c1.$c2##$c3",
+"$c3         ",
+],
+'[' => [
+"$c3     ",
+"$c1.$c2####$c3",
+"$c1.$c2##$c3  ",
+"$c1.$c2##$c3  ",
+"$c1.$c2##$c3  ",
+"$c1.$c2##$c3  ",
+"$c1.$c2##$c3  ",
+"$c1.$c2####$c3",
+"$c3     ",
+],
+']' => [
+"$c3     ",
+"$c1.$c2####$c3",
+"$c3  $c1.$c2##$c3",
+"$c3  $c1.$c2##$c3",
+"$c3  $c1.$c2##$c3",
+"$c3  $c1.$c2##$c3",
+"$c3  $c1.$c2##$c3",
+"$c1.$c2####$c3",
+"$c3     ",
+],
+'{' => [
+"$c3     ",
+"$c3 $c1.$c2###$c3",
+"$c1.$c2##$c3  ",
+"$c3 $c1.$c2##$c3 ",
+"$c1.$c2##$c3  ",
+"$c3 $c1.$c2##$c3 ",
+"$c1.$c2##$c3  ",
+"$c3 $c1.$c2###$c3",
+"$c3     ",
+],
+'}' => [
+"$c3     ",
+"$c1.$c2###$c3 ",
+"$c3  $c1.$c2##$c3",
+"$c3 $c1.$c2##$c3 ",
+"$c3  $c1.$c2##$c3",
+"$c3 $c1.$c2##$c3 ",
+"$c3  $c1.$c2##$c3",
+"$c1.$c2###$c3 ",
+"$c3     ",
+],
+':' => [
+"$c3     ",
+"$c3     ",
+"$c3     ",
+"$c3 $c1.$c2##$c3 ",
+"$c3     ",
+"$c3     ",
+"$c3 $c1.$c2##$c3 ",
+"$c3     ",
+"$c3     ",
+],
+';' => [
+"$c3     ",
+"$c3     ",
+"$c3     ",
+"$c3 $c1.$c2##$c3 ",
+"$c3     ",
+"$c3     ",
+"$c3 $c1.$c2##$c3 ",
+"$c3  $c1.$c2#$c3 ",
+"$c3     ",
+],
+'\'' => [
+"$c3    ",
+"$c3 $c1.$c2##",
+"$c1.$c2##$c3 ",
+"$c3    ",
+"$c3    ",
+"$c3    ",
+"$c3    ",
+"$c3    ",
+"$c3    ",
+],
+'"' => [
+"$c3       ",
+"$c1.$c2##$c3 $c1.$c2##$c3",
+"$c1.$c2##$c3 $c1.$c2##$c3",
+"$c3       ",
+"$c3       ",
+"$c3       ",
+"$c3       ",
+"$c3       ",
+"$c3       ",
+],
+'<' => [
+"$c3       ",
+"$c3       ",
+"$c3    $c1.$c2##$c3",
+"$c3  $c1.$c2##$c3  ",
+"$c1.$c2##$c3    ",
+"$c3  $c1.$c2##$c3  ",
+"$c3    $c1.$c2##$c3",
+"$c3       ",
+"$c3       ",
+],
+'>' => [
+"$c3       ",
+"$c3       ",
+"$c1.$c2##$c3    ",
+"$c3  $c1.$c2##$c3  ",
+"$c3    $c1.$c2##$c3",
+"$c3  $c1.$c2##$c3  ",
+"$c1.$c2##$c3    ",
+"$c3       ",
+"$c3       ",
+],
+'?' => [
+"$c3         ",
+"$c3  $c1.$c2#####$c3 ",
+"$c3 $c1.$c2##$c3  $c1.$c2##",
+"$c3     $c1.$c2##$c3 ",
+"$c3    $c1.$c2##$c3  ",
+"$c3   $c1.$c2##$c3   ",
+"$c3         ",
+"$c3 $c1.$c2##$c3     ",
+"$c3         ",
+],
+"\," => [
+"$c3   ",
+"$c3   ",
+"$c3   ",
+"$c3   ",
+"$c3   ",
+"$c3   ",
+"$c3   ",
+"$c1.$c2##$c3",
+"$c3 $c1.$c2#$c3",
+],
+"\." => [
+"$c3   ",
+"$c3   ",
+"$c3   ",
+"$c3   ",
+"$c3   ",
+"$c3   ",
+"$c3   ",
+"$c1.$c2##",
+"$c3   ",
+],
+"\/" => [
+"$c3         ",
+"$c3      $c1.$c2##$c3",
+"$c3     $c1.$c2##$c3 ",
+"$c3    $c1.$c2##$c3  ",
+"$c3   $c1.$c2##$c3   ",
+"$c3  $c1.$c2##$c3    ",
+"$c3 $c1.$c2##$c3     ",
+"$c1.$c2##$c3      ",
+"$c3         ",
+],
+);
+}
diff --git a/irc/weechat/masshl.py b/irc/weechat/masshl.py
@@ -0,0 +1,106 @@
+# Released into the Public Domain
+
+import random
+#from threading import Thread
+#from time import sleep
+import weechat
+
+SCRIPT_NAME    = "masshl"
+SCRIPT_AUTHOR  = "The Krusty Krab <wowaname@volatile.ch>"
+SCRIPT_VERSION = "1.0"
+SCRIPT_LICENSE = "Public domain"
+SCRIPT_DESC    = "Provides nicklist hooks."
+
+if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION,
+	SCRIPT_LICENSE, SCRIPT_DESC, "", ""):
+	weechat.hook_command("masshl",
+		SCRIPT_DESC,
+		"[-do <delay>] text (broken, currently no-op)",
+		"-d  Specify a delay at the beginning (e.g. -d 1 for\n"
+		"    one second) to insert a delay between messages.\n"
+		"    %n - replace with next nick\n"
+		"    %N - replace with as many nicks as possible per line\n"
+		"    %r - replace with random hex value to thwart antispam\n"
+		"-o  Include your own nick in output",
+		"-od", "masshl_cmd_cb", "")
+
+class Loop():
+	def __init__(self, buffer, nicks, input, input_method, N_param, delay, opts):
+		self.buffer = buffer
+		self.nicks = nicks
+		self.input = input
+		self.input_method = input_method
+		self.N_param = N_param
+		self.delay = delay
+		self.opts = opts
+
+	def run(self):
+		i = -('o' not in self.opts)
+		if i == -1: self.nicks.pop(0)
+		N_nicks = ""
+		output = self.input
+		for nick in self.nicks:
+			i += 1
+			if self.N_param:
+				N_nicks += " %s" % nick
+				if (nick != self.nicks[-1] and
+				 len(output) + len(N_nicks) + len(self.nicks[i]) < 300):
+					continue
+			else: output = self.input.replace("%n",nick)
+			N_nicks = N_nicks.lstrip()
+			output = output.replace("%N",N_nicks)
+			output = output.replace("%r","%08x" % random.randint(0,0xffffffff))
+			if self.input_method == "keybinding":
+				weechat.buffer_set(self.buffer, "input", output)
+			else:
+				weechat.command(self.buffer, output)
+#			sleep(self.delay)
+			output = self.input
+			N_nicks = ""
+
+def masshl_cmd_cb(data, buffer, args):
+	input = args
+
+	input_method = "command"
+	server = weechat.buffer_get_string(buffer, 'localvar_server')
+	channel = weechat.buffer_get_string(buffer, 'localvar_channel')
+
+	if not input or (input[0] == '-' and input.find(' ') == -1):
+		input = (input + ' ' if input else '') + weechat.buffer_get_string(buffer, "input")
+		input_method = "keybinding"
+
+	N_param = "%N" in input
+	if not N_param and "%n" not in input and "%r" not in input:
+		# if we bind this to Enter key, we don't want useless flooding on
+		# normal messages
+		return weechat.WEECHAT_RC_OK
+
+	optstop = input and input[0] == '-' and input.find(' ')
+	opts = input[1:optstop] if optstop else ''
+	cmdstop = 'd' in opts and input.find(' ', optstop+1)
+	delay = 0
+	if 'd' in opts:
+		find = input[optstop+1:cmdstop]
+		where = input.find(find, cmdstop+1)
+		try: delay = float(find)
+		except ValueError:
+			weechat.prnt(buffer, "delay must be a float value!")
+			return weechat.WEECHAT_RC_ERROR
+		input = input[where+len(find):]
+	else: input = input[optstop+bool(optstop):]
+
+	nicklist = weechat.infolist_get("irc_nick", "", "%s,%s" % (server,channel))
+
+	# dealing with the cursor can get a little tricky. let's use a dict
+	# instead, that way we can manipulate just what we need and we can
+	# do that with builtins
+	nicks = []
+	while weechat.infolist_next(nicklist):
+		nicks.append(weechat.infolist_string(nicklist, "name"))
+
+	weechat.infolist_free(nicklist)
+
+	workhorse = Loop(buffer, nicks, input, input_method, N_param, delay, opts)
+	workhorse.run()
+
+	return weechat.WEECHAT_RC_OK
diff --git a/irc/weechat/parrot.pl b/irc/weechat/parrot.pl
@@ -0,0 +1,380 @@
+use strict;
+use warnings;
+
+no strict 'subs';
+
+my $SCRIPT_NAME = 'parrot';
+my $SCRIPT_AUTHOR = 'The Krusty Krab <wowaname@volatile.ch>';
+my $SCRIPT_VERSION = '1.0';
+my $SCRIPT_LICENCE = 'Public domain';
+my $SCRIPT_DESC = 'Relay channel messages and modes';
+
+# %cbs{server}{hook_name} = $hook_ptr:
+#     stores hook pointers to unhook() on exit
+# %chans{server}{channel} = @groups:
+#     stores groups associated with a channel
+# %groups{groupname}{server}{channel} = $flags:
+#     stores channels associated with a group, as well as the channel's flags
+# $READ, $STAT, $MODE:
+#     flags for -read, -stat, -mode switches
+our (%cbs, %chans, %groups);
+our ($READ, $STAT, $MODE) = (0x1, 0x2, 0x4);
+our $confpath;
+
+sub servchan
+{
+	my $buffer = shift;
+	return (lc weechat::buffer_get_string($buffer, 'localvar_server'),
+		lc weechat::buffer_get_string($buffer, 'localvar_channel'));
+}
+
+sub ircbuf { weechat::buffer_search('irc', "(?i)".(join '.', @_)) }
+
+sub getgroup
+{
+	my ($server, $channel) = @_;
+	my @ret;
+
+	for my $group (@{ $chans{$server}{$channel} }) {
+	for my $to_serv (keys %{ $groups{$group} }) {
+	for my $to_chan (keys %{ $groups{$group}{$to_serv} }) {
+		# don't send to myself
+		next if $to_serv eq $server and $to_chan eq $channel;
+		push @ret, [$to_serv, $to_chan, $groups{$group}{$to_serv}{$to_chan}, $group]
+	} } }
+
+	return @ret;
+}
+
+sub sendto
+{
+	my ($server, $command) = @_;
+	weechat::hook_signal_send('irc_input_send',
+		weechat::WEECHAT_HOOK_SIGNAL_STRING,
+		"$server;;1;;$command");
+}
+
+sub add_relay
+{
+	my ($groupname, $server, $channel, $flags) = @_;
+	return if exists $cbs{$server};
+	push @{ $chans{$server}{$channel} }, $groupname;
+	$groups{$groupname}{$server}{$channel} = $flags;
+	$cbs{$server}{PRIVMSG} =
+		weechat::hook_signal("$server,irc_raw_in_privmsg", 'irc_privmsg_notice', '');
+	$cbs{$server}{NOTICE} =
+		weechat::hook_signal("$server,irc_raw_in_notice", 'irc_privmsg_notice', '');
+	$cbs{$server}{OUT_PRIVMSG} =
+		weechat::hook_signal("$server,irc_out1_privmsg", 'ircout_privmsg_notice', '');
+	$cbs{$server}{OUT_NOTICE} =
+		weechat::hook_signal("$server,irc_out1_notice", 'ircout_privmsg_notice', '');
+	if ($flags & $STAT) {
+		$cbs{$server}{JOIN} =
+			weechat::hook_signal("$server,irc_raw_in_join", 'irc_join', '');
+		$cbs{$server}{PART} =
+			weechat::hook_signal("$server,irc_raw_in_part", 'irc_part', '');
+		$cbs{$server}{KICK} =
+			weechat::hook_signal("$server,irc_raw_in_kick", 'irc_kick', '');
+		$cbs{$server}{NICK} =
+			weechat::hook_signal("$server,irc_raw_in_nick", 'irc_nick', '');
+		$cbs{$server}{QUIT} =
+			weechat::hook_signal("$server,irc_raw_in_quit", 'irc_quit', '');
+	}
+	if ($flags & $MODE) {
+#		$cbs{$server}{MODE} =
+#			weechat::hook_signal("$server,irc_raw_in_mode", 'irc_mode', '');
+		$cbs{$server}{TOPIC} =
+			weechat::hook_signal("$server,irc_raw_in_topic", 'irc_topic', '');
+	}
+}
+
+sub read_conf
+{
+	open FH, '<', $confpath or weechat::print('', weechat::prefix('error').
+		"Error opening $confpath for reading: $!"), return;
+	while (<FH>) {
+		chomp;
+		add_relay(split ' ');
+	}
+	close FH;
+}
+
+sub write_conf
+{
+	open FH, '>', $confpath or weechat::print('', weechat::prefix('error').
+		"Error opening $confpath for writing: $!"), return;
+	for my $server (keys %chans) {
+	for my $channel (keys %{ $chans{$server} }) {
+	for my $group (@{ $chans{$server}{$channel} }) {
+		my $flags = $groups{$group}{$server}{$channel};
+		print FH "$group $server $channel $flags\n";
+	} } }
+	close FH;
+}
+
+sub irc_privmsg_notice
+{
+	my (undef, $server, $cmd, $nick, $channel, $message) = (shift,
+		shift =~ /(.+),irc_raw_in_(privmsg|notice)/i,
+		shift =~ /:([^! ]*)[^ ]* [^ ]+ ([^ ]+) :?(.*)/i);
+	($server, $channel) = (lc $server, lc $channel);
+	return weechat::WEECHAT_RC_OK unless exists $chans{$server}{$channel};
+
+	for (getgroup($server, $channel)) {
+		my ($to_serv, $to_chan, $flags, undef) = @$_;
+		next if $flags & $READ;
+		next unless ircbuf("$to_serv.$to_chan");
+		if ($message =~ /^\x01ACTION /i) {
+			$message =~ s/^\x01ACTION |\x01$//g;
+			sendto($to_serv, "/msg $to_chan * \x02$nick\x0f $message");
+			next;
+		}
+		my $prefix = lc $cmd eq 'notice' ? "[\x02$nick\x0f]" : "<\x02$nick\x0f>";
+		sendto($to_serv, "/msg $to_chan $prefix $message");
+	}
+
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub ircout_privmsg_notice
+{
+	my (undef, $server, $cmd, $channel, $message) = (shift,
+		shift =~ /(.*),irc_out1_(privmsg|notice)/i,
+		shift =~ /[^ ]+ ([^ ]+) :?(.*)/i);
+	($server, $channel) = (lc $server, lc $channel);
+	return weechat::WEECHAT_RC_OK unless exists $chans{$server}{$channel};
+
+	for (getgroup($server, $channel)) {
+		my ($to_serv, $to_chan, $flags, undef) = @$_;
+		next if $flags & $READ;
+		next unless ircbuf("$to_serv.$to_chan");
+		my $prefix = lc $cmd eq 'notice' ? 'notice' : 'msg';
+		if ($message =~ /^\x01ACTION /i) {
+			$message =~ s/^\x01ACTION |\x01$//g;
+			sendto($to_serv, "/$prefix $to_chan \x01ACTION $message\x01");
+			next;
+		}
+		sendto($to_serv, "/$prefix $to_chan $message");
+	}
+
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub irc_join
+{
+	my (undef, $server, $nick, $host, $channel) = (shift,
+		shift =~ /(.+),irc_raw_in_join/i,
+		shift =~ /:([^! ]*)([^ ]*) join :?([^ ]+)/i);
+	($server, $channel) = (lc $server, lc $channel);
+	return weechat::WEECHAT_RC_OK unless exists $chans{$server}{$channel};
+
+	for (getgroup($server, $channel)) {
+		my ($to_serv, $to_chan, $flags, undef) = @$_;
+		next unless $flags & $STAT;
+		next if $flags & $READ;
+		next unless ircbuf("$to_serv.$to_chan");
+		sendto($to_serv, "/notice $to_chan \x02$nick\x0f$host joined $server/$channel\x0f");
+	}
+
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub irc_part
+{
+	my (undef, $server, $nick, $channel, $message) = (shift,
+		shift =~ /(.+),irc_raw_in_part/i,
+		shift =~ /:([^! ]*)[^ ]* part ([^ ]+) ?:?(.*)/i);
+	($server, $channel) = (lc $server, lc $channel);
+	return weechat::WEECHAT_RC_OK unless exists $chans{$server}{$channel};
+
+	for (getgroup($server, $channel)) {
+		my ($to_serv, $to_chan, $flags, undef) = @$_;
+		next unless $flags & $STAT;
+		next if $flags & $READ;
+		next unless ircbuf("$to_serv.$to_chan");
+		sendto($to_serv, "/notice $to_chan \x02$nick\x0f left $server/$channel\x0f: $message");
+	}
+
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub irc_kick
+{
+	my (undef, $server, $nick, $channel, $target, $message) = (shift,
+		shift =~ /(.+),irc_raw_in_kick/i,
+		shift =~ /:([^! ]*)[^ ]* kick ([^ ]+) ([^ ]+) :?(.*)/i);
+	($server, $channel) = (lc $server, lc $channel);
+	return weechat::WEECHAT_RC_OK unless exists $chans{$server}{$channel};
+
+	for (getgroup($server, $channel)) {
+		my ($to_serv, $to_chan, $flags, undef) = @$_;
+		next unless $flags & $STAT;
+		next if $flags & $READ;
+		next unless ircbuf("$to_serv.$to_chan");
+		sendto($to_serv, "/notice $to_chan \x02$nick\x0f kicked $target\x0f from $server/$channel\x0f: $message");
+	}
+
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub irc_nick
+{
+	my (undef, $server, $nick, $newnick) = (shift,
+		shift =~ /(.+),irc_raw_in_nick/i,
+		shift =~ /:([^! ]*)[^ ]* nick :?(.*)/i);
+
+	for my $channel (keys %{ $chans{$server} }) {
+	my $iptr = weechat::infolist_get('irc_nick', '', "$server,$channel,$nick");
+	next unless $iptr;
+	weechat::infolist_free($iptr);
+	for (getgroup($server, $channel)) {
+		my ($to_serv, $to_chan, $flags, undef) = @$_;
+		next unless $flags & $STAT;
+		next if $flags & $READ;
+		next unless ircbuf("$to_serv.$to_chan");
+		sendto($to_serv, "/notice $to_chan \x02$nick\x0f is now \x02$newnick\x0f");
+	} }
+
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub irc_quit
+{
+	my (undef, $server, $nick, $message) = (shift,
+		shift =~ /(.+),irc_raw_in_quit/i,
+		shift =~ /:([^! ]*)[^ ]* quit :?(.*)/i);
+
+	for my $channel (keys %{ $chans{$server} }) {
+	my $iptr = weechat::infolist_get('irc_nick', '', "$server,$channel,$nick");
+	next unless $iptr;
+	weechat::infolist_free($iptr);
+	for (getgroup($server, $channel)) {
+		my ($to_serv, $to_chan, $flags, undef) = @$_;
+		next unless $flags & $STAT;
+		next if $flags & $READ;
+		next unless ircbuf("$to_serv.$to_chan");
+		sendto($to_serv, "/notice $to_chan \x02$nick\x0f left $server: $message");
+	} }
+
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub irc_mode
+{
+	my (undef, $server, $nick, $channel, $modes) = (shift,
+		shift =~ /(.+),irc_raw_in_mode/i,
+		shift =~ /:([^! ]*)[^ ]* mode ([^ ]+) (.*)/i);
+	($server, $channel) = (lc $server, lc $channel);
+	return weechat::WEECHAT_RC_OK unless exists $chans{$server}{$channel};
+
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub irc_topic
+{
+	my (undef, $server, $nick, $channel, $message) = (shift,
+		shift =~ /(.+),irc_raw_in_topic/i,
+		shift =~ /:([^! ]*)[^ ]* topic ([^ ]+) :?([^ ]+)/i);
+	($server, $channel) = (lc $server, lc $channel);
+	weechat::print('',"$server $channel");
+	return weechat::WEECHAT_RC_OK unless exists $chans{$server}{$channel};
+	return weechat::WEECHAT_RC_OK if lc $nick eq lc weechat::info_get('irc_nick', $server);
+
+	for (getgroup($server, $channel)) {
+		my ($to_serv, $to_chan, $flags, undef) = @$_;
+		next unless $flags & $MODE;
+		next if $flags & $READ;
+		next unless ircbuf("$to_serv.$to_chan");
+		sendto($to_serv, "/topic $to_chan $message");
+	}
+
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub cmd_parrot
+{
+	my (undef, $buffer, $command) = @_;
+	my ($server, $channel) = servchan($buffer);
+	my ($flags, $remove, $groupname) =
+	   (     0,       0,         '');
+	for (split / +/, $command) {
+		/^-read$/   and ($flags |= $READ), next;
+		/^-stat$/   and ($flags |= $STAT), next;
+		/^-mode$/   and ($flags |= $MODE), next;
+		/^-remove$/ and ($remove = 1), next;
+		$groupname = $_; last;
+	}
+
+	unless ($groupname) {
+		if ($chans{$server}{$channel}) {
+			for (getgroup($server, $channel)) {
+				my ($to_serv, $to_chan, $flags, $group) = @$_;
+				my $flag_str = $flags ? ':' : '';
+				$flag_str .= ' readonly' if $flags & $READ;
+				$flag_str .= ' statusmsg' if $flags & $STAT;
+				$flag_str .= ' sendmodes' if $flags & $MODE;
+				weechat::print($buffer, weechat::prefix('server').
+					"Relaying to $to_serv/$to_chan in group $group$flag_str");
+			}
+		} else {
+			weechat::print($buffer, weechat::prefix('server').
+				"This channel is not being relayed");
+		}
+		return weechat::WEECHAT_RC_OK;
+	}
+
+	# clear hooks first (if they exist)
+	if (exists $cbs{$server}) {
+		weechat::unhook($cbs{$server}{$_}) for (keys %{ $cbs{$server} });
+		delete $cbs{$server};
+	}
+	@{ $chans{$server}{$channel} } =
+		grep { $_ ne $groupname } @{ $chans{$server}{$channel} };
+
+	if ($remove) {
+		delete $groups{$groupname}{$server}{$channel};
+		delete $groups{$groupname}{$server} unless $groups{$groupname}{$server};
+		delete $groups{$groupname} unless $groups{$groupname};
+		delete $chans{$server}{$channel} unless $chans{$server}{$channel};
+		delete $chans{$server} unless $chans{$server};
+
+		write_conf();
+		weechat::print($buffer, weechat::prefix('server').
+			"Removed relay from group $groupname");
+		return weechat::WEECHAT_RC_OK;
+	}
+
+	add_relay($groupname, $server, $channel, $flags);
+
+	write_conf();
+	weechat::print($buffer, weechat::prefix('server').
+		"Added relay to group $groupname");
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub completion_groupnames
+{
+	my $completion = pop;
+	weechat::hook_completion_list_add($completion, $_, 0,
+		weechat::WEECHAT_LIST_POS_SORT) for keys %groups;
+}
+
+if (weechat::register($SCRIPT_NAME, $SCRIPT_AUTHOR, $SCRIPT_VERSION,
+ $SCRIPT_LICENCE, $SCRIPT_DESC, '', '')) {
+	$confpath = weechat::info_get('weechat_dir', '') . '/parrot.db';
+	weechat::hook_completion('perl_parrot_groupname', 'parrot.pl group names',
+		'completion_groupnames', '');
+	weechat::hook_command('parrot', $SCRIPT_DESC,
+		"[-read] [-stat] [-mode] groupname\n".
+		"-remove",
+		"-read: relay from this channel to others, but do not relay to\n".
+		"       this channel\n".
+		"-stat: show status messages (join/part) in this channel\n".
+		"-mode: transfer modes to this channel, even if you are op".
+		"groupname: all channels with the same group name are relayed together\n".
+		"-remove: remove this channel from the relay group",
+		'-remove %(perl_parrot_groupname) %-'.
+		'||-read|-stat|-mode|%(perl_parrot_groupname)|%*',
+		'cmd_parrot', '');
+	read_conf();
+}
diff --git a/irc/weechat/play.pl b/irc/weechat/play.pl
@@ -0,0 +1,156 @@
+use strict;
+use warnings;
+use File::Find::Rule;
+no strict 'subs';
+
+my $SCRIPT_NAME = 'play';
+my $SCRIPT_AUTHOR = 'The Krusty Krab <wowaname@volatile.ch>';
+my $SCRIPT_VERSION = '1.2';
+my $SCRIPT_LICENCE = 'Public domain';
+my $SCRIPT_DESC = 'Play ASCII art';
+our (%queue, %timer);
+
+if (weechat::register($SCRIPT_NAME, $SCRIPT_AUTHOR, $SCRIPT_VERSION,
+ $SCRIPT_LICENCE, $SCRIPT_DESC, '', '')) {
+	weechat::hook_command('play', 'Play ASCII art',
+		'[-delay ms] [-repeat times] [-pipe "command"] [-fmt "list"] filename'.
+		"\n-find pattern\n-stop\n",
+		"-delay: delay in milliseconds between lines\n".
+		"-find: list matching files, don't play\n".
+		"-pipe: pipe output into command\n".
+		"-fmt: treat file as a format string and replace with arguments in\n".
+		"    list. Arguments are separated by semicolons (;)\n".
+		"filename: file to play. Supports wildcards. By default, searches\n".
+		"    subdirectories as well unless '/' is found in the filename\n".
+		"-stop: stop currently playing file in buffer",
+		'-delay|-pipe|-fmt|-repeat|%*'.
+		' || -find'.
+		' || -stop',
+		'cmd_play', '');
+
+	my %OPTIONS = (
+		delay => ['Default delay between lines, in milliseconds', 0],
+		dir => ['Art directory',
+			weechat::info_get('weechat_dir', '').'/ascii'],
+		find_limit => ['Maximum number of results returned by -find. '.
+			'-1 = unlimited (may lock up WeeChat with many results!)', 32],
+		);
+
+	for my $option (keys %OPTIONS) {
+		weechat::config_set_plugin($option, $OPTIONS{$option}[1])
+		 unless weechat::config_is_set_plugin($option);
+		weechat::config_set_desc_plugin($option, $OPTIONS{$option}[0]);
+	}
+}
+
+sub parse
+{
+	my ($input, $delay, $pipe, $find, $repeat, $fmt) =
+		(shift, weechat::config_get_plugin('delay'), '/msg *', 0, 1, '');
+
+	if ($input =~ / *-delay +([0-9]+) /) {
+		$delay = $1;
+		$input =~ s/-delay +[0-9]+//;
+	}
+	if ($input =~ / *-find /) {
+		$find = 1;
+		$input =~ s/-find//;
+	}
+	if ($input =~ / *-fmt +("(?:[^"\\]|\\.)+"|[^ ]+) /) {
+		$fmt = $1;
+		$fmt =~ s/^"(.+)"$/$1/ if $fmt =~ /^".+"$/;
+		$input =~ s/-fmt +("(?:[^"\\]|\\.)+"|[^ ]+)//;
+	}
+	if ($input =~ / *-repeat +([0-9]+) /) {
+		$repeat = $1;
+		$input =~ s/-repeat +[0-9]+//;
+	}
+	if ($input =~ / *-pipe +("(?:[^"\\]|\\.)+"|[^ ]+) /) {
+		$pipe = $1;
+		$pipe =~ s/^"(.+)"$/$1/ if $pipe =~ /^".+"$/;
+		$input =~ s/-pipe +("(?:[^"\\]|\\.)+"|[^ ]+)//;
+	}
+
+	return ($delay, $pipe, $find, $repeat, $fmt, $input =~ s/^ +| +$//r);
+}
+
+sub play
+{
+	my $buffer = shift;
+
+	weechat::command($buffer, shift @{ $queue{$buffer} });
+	delete $queue{$buffer} unless @{ $queue{$buffer} };
+
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub cmd_play
+{
+	my $buffer = $_[1];
+
+	if ($_[2] eq '-stop') {
+		if (exists $timer{$buffer}) {
+			weechat::unhook($timer{$buffer});
+			delete $queue{$buffer};
+		}
+		return weechat::WEECHAT_RC_OK;
+	}
+
+	my ($delay, $pipe, $find, $repeat, $fmt, $file) = parse($_[2]);
+	my $server = weechat::info_get($buffer, 'localvar_server');
+	my ($prio_s, $prio_d) = (
+		weechat::config_get("irc.server.$server.anti_flood_prio_high"),
+		weechat::config_get("irc.server_default.anti_flood_prio_high"),
+		);
+	$delay = ($delay or 1000 * (
+		weechat::config_option_is_null($prio_s)
+		? weechat::config_integer($prio_d)
+		: weechat::config_integer($prio_s)
+		) or 10);
+
+	my $rule = File::Find::Rule
+		->file
+		->name($file)
+		->start(weechat::config_get_plugin('dir'));
+
+	if ($find) {
+		my $i = weechat::config_get_plugin('find_limit');
+		weechat::print($buffer, " \t$_")
+			while defined( $_ = $rule->match ) and --$i;
+		weechat::print($buffer, weechat::prefix('error').
+			"Too many results; please narrow your search") unless $i;
+		weechat::print($buffer, " \tEnd of file listing for '$file'");
+		return weechat::WEECHAT_RC_OK;
+	}
+
+	my $path;
+	if ($file =~ m"/") { $path = weechat::config_get_plugin('dir')."/$file" }
+	else { $path = $rule->match }
+
+	if ($path and -z $path) {
+		weechat::print($buffer, weechat::prefix('error').
+			"File '$file' is empty");
+	} elsif ($path and open FH, "<", $path) {
+		my @lines;
+		while (<FH>) {
+			no warnings; # sprintf barks if there's nothing to replace
+			$_ = sprintf $_, split ';', $fmt if $fmt;
+			push @lines, s/[\r\n]*$//r
+		}
+		close FH;
+		for (1 .. $repeat) {
+			push @{ $queue{$buffer} }, "$pipe \x0f$_\x0f" for @lines;
+		}
+
+		weechat::unhook($timer{$buffer}) if exists $timer{$buffer};
+		$timer{$buffer} =
+			weechat::hook_timer($delay, 0, scalar @{ $queue{$buffer} },
+			'play', $buffer);
+	} else {
+		weechat::print($buffer, weechat::prefix('error').
+			"Cannot open '$file'".($! ? ": $!" : ""));
+		return weechat::WEECHAT_RC_ERROR;
+	}
+
+	return weechat::WEECHAT_RC_OK;
+}
diff --git a/irc/weechat/prismx.py b/irc/weechat/prismx.py
@@ -0,0 +1,138 @@
+# Copyright (c) 2010 Alex Barrett <al.barrett@gmail.com>
+#
+# Everyone is permitted to copy and distribute verbatim or modified
+# copies of this license document, and changing it is allowed as long
+# as the name is changed.
+#
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+#
+# 0. You just DO WHAT THE FUCK YOU WANT TO.
+
+import weechat as w
+import random
+import re
+
+SCRIPT_NAME	= "prismx"
+SCRIPT_AUTHOR  = "Alex Barrett <al.barrett@gmail.com>"
+SCRIPT_VERSION = "0.3.1"
+SCRIPT_LICENSE = "WTFPL"
+SCRIPT_DESC	= "Taste the rainbow."
+
+# red, lightred, brown, yellow, green, lightgreen, cyan,
+# lightcyan, blue, lightblue, magenta, lightmagenta
+ncolors = [5, 4, 7, 8, 3, 9, 10, 11, 2, 12, 6, 13]
+xcolors = [
+16,28,40,52,64,65,53,41,29,17,18,30,42,54,66,67,55,43,31,19,20,32,44,
+56,68,69,57,45,33,21,22,34,46,58,70,71,59,47,35,23,24,36,48,60,72,73,
+61,49,37,25,26,38,50,62,74,75,63,51,39,27]
+xxcolors = range(100)
+
+# we set this later
+color_count = 0
+
+# keeping a global index means the coloring will pick up where it left off
+color_index = 0
+
+# spaces don't need to be colored and commas cannot be because mIRC is dumb
+chars_neutral = " ,"
+chars_control = "\x01-\x1f\x7f-\x9f"
+
+regex_chars = "[^%(n)s%(s)s][%(n)s%(s)s]*" % { 'n': chars_neutral, 's': chars_control }
+regex_words = "[^%(n)s]+[%(n)s%(s)s]*" % { 'n': chars_neutral, 's': chars_control }
+
+
+if w.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION,
+			  SCRIPT_LICENSE, SCRIPT_DESC, "", ""):
+	w.hook_command("prism",
+		SCRIPT_DESC,
+		"[-rwmbexsp] [palette] text|-c[wbexsp] [palette] <sep> <command> <sep>text",
+		"    -r: randomizes the order of the color sequence\n"
+		"    -w: color entire words instead of individual characters\n"
+		"    -m: append /me to beginning of output\n"
+		"    -b: backwards text (entire string is reversed)\n"
+		"    -e: eye-destroying colors (randomized background colors)\n"
+		"    -c: specify a separator to turn on colorization\n"
+		"        eg. -c : /topic :howdy howdy howdy\n"
+		"    -x: extended color set, requires 256color terminal\n"
+		"    -s: stretch to fit text\n"
+		"    -p: specify color palette to use, comma separated\n"
+		"  text: text to be colored",
+		"-r|-w|-m|-b|-e|-c", "prism_cmd_cb", "")
+
+def prism_cmd_cb(data, buffer, args):
+	global color_index
+	color_local = color_index
+	color_index += 1
+
+	input = args.decode("UTF-8")
+	input_method = "command"
+
+	if not input or (input[0] == '-' and input.find(' ') == -1):
+		input = (input + ' ' if input else '') + w.buffer_get_string(buffer, "input")
+		input = input.decode("UTF-8")
+		input_method = "keybinding"
+
+	if not input:
+		return w.WEECHAT_RC_OK
+
+	optstop = input and input[0] == '-' and input.find(' ')
+	opts = input[1:optstop] if optstop else ''
+	cmdstop = 'c' in opts and input.find(' ', optstop+1)
+	cmd = ''
+	if 'm' in opts: cmd = '/me '
+	if 'c' in opts:
+		find = input[optstop+1:cmdstop]
+		where = input.find(find, cmdstop+1)
+		cmd = input[cmdstop+1:where]
+		input = input[where+len(find):]
+	else:
+		input = input[optstop+bool(optstop):]
+	regex = regex_words if 'w' in opts else regex_chars
+	inc = 'r' not in opts
+	bs = 'e' in opts
+	colors = ncolors if 'x' not in opts else (xxcolors if bs or not inc else xcolors)
+	if 'p' in opts:
+		i = input.find(' ')
+		colors = input[:i].split(',')
+		input = input[i+1:]
+	input = input[::-1] if 'b' in opts else input
+	output = u""
+	tokens = re.findall(regex, input)
+
+	if 's' in opts:
+		color_local = 0
+		colors = [colors[int(float(i)/len(tokens)*len(colors))]
+		 for i in xrange(len(tokens))]
+
+	color_count = len(colors)
+	for token in tokens:
+		# prefix each token with a color code
+		c1 = unicode(colors[color_local % color_count]).rjust(2, "0")
+		if bs:
+			c2 = random.randint(1, color_count - 1) % color_count
+			c2 = unicode(colors[c2 + 1 if c2 == color_local % color_count else c2]).rjust(2,"0")
+			output += u'\x03' + c1 + ',' + c2 + token
+		else:
+			output += u"\x03" + c1 + token
+
+		# select the next color or another color at
+		# random depending on the options specified
+		if not inc:
+			color_local += random.randint(1, color_count - 1)
+		else:
+			color_local += inc
+	output += u'\x0f'
+
+	# output starting with a / will be executed as a
+	# command unless we escape it with a preceding /
+	# Commands should use the -c flag
+	if len(output) > 0 and output[0] == "/":
+		output = "/" + output
+	if len(cmd) > 0:
+		output = cmd + output
+	if input_method == "keybinding":
+		w.buffer_set(buffer, "input", output.encode("UTF-8"))
+	else:
+		w.command(buffer, output.encode("UTF-8"))
+	return w.WEECHAT_RC_OK
diff --git a/irc/weechat/sighup.pl b/irc/weechat/sighup.pl
@@ -0,0 +1,22 @@
+use strict;
+use warnings;
+
+no strict 'subs';
+
+my $SCRIPT_NAME = 'sighup';
+my $SCRIPT_AUTHOR = 'The Krusty Krab <wowaname@volatile.ch>';
+my $SCRIPT_VERSION = '1.0';
+my $SCRIPT_LICENCE = 'Public domain';
+my $SCRIPT_DESC = 'Reload config on SIGHUP';
+
+if (weechat::register($SCRIPT_NAME, $SCRIPT_AUTHOR, $SCRIPT_VERSION,
+ $SCRIPT_LICENCE, $SCRIPT_DESC, '', '')) {
+	weechat::hook_signal('signal_sighup', 'cb_sighup', '');
+}
+
+sub cb_sighup {
+	weechat::command('', '/reload');
+
+	return weechat::WEECHAT_RC_OK_EAT;
+}
+
diff --git a/irc/weechat/snomasks.pl b/irc/weechat/snomasks.pl
@@ -0,0 +1,58 @@
+# Released into the Public Domain
+
+# Note: After loading the script and adding snomasks into one of your bars, you
+# must request /umode once on each server you have server notices masks set on.
+# After that, the script will automatically update the bar item.
+
+use strict;
+use warnings;
+
+no strict 'subs';
+
+my $SCRIPT_NAME = 'snomasks';
+my $SCRIPT_AUTHOR = 'The Krusty Krab <wowaname@volatile.ch>';
+my $SCRIPT_VERSION = '1.1';
+my $SCRIPT_LICENCE = 'Public domain';
+my $SCRIPT_DESC = 'Server notice mask bar item for opers';
+
+if (weechat::register($SCRIPT_NAME, $SCRIPT_AUTHOR, $SCRIPT_VERSION,
+ $SCRIPT_LICENCE, $SCRIPT_DESC, '', '')) {
+	weechat::bar_item_new('snomasks', 'bar_snomasks', '');
+	weechat::hook_signal('buffer_switch', 'buffer_switch', '');
+	weechat::hook_signal('irc_server_disconnected', 'irc_disconnected', '');
+	weechat::hook_signal('*,irc_raw_in_008', 'irc_008', '');
+}
+
+my %snomask;
+
+sub bar_snomasks {
+	my $buffer = weechat::current_buffer();
+
+	return ''
+		if weechat::buffer_get_string($buffer, 'localvar_plugin') ne 'irc';
+
+	my $server = weechat::buffer_get_string($buffer, 'localvar_server');
+	return $snomask{$server} // '';
+}
+
+sub buffer_switch {
+	weechat::bar_item_update('snomasks');
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub irc_008 {
+	my (undef, $server, $modes) = (shift,
+		shift =~ /^(.+),irc_raw_in_008$/,
+		shift =~ /:[^ ]* 008 [^ ]* (?::Server notice mask \()?([^ )]*)/);
+	$server = lc $server;
+
+	$snomask{$server} = $modes;
+	weechat::bar_item_update('snomasks');
+	return weechat::WEECHAT_RC_OK;
+}
+
+sub irc_disconnected {
+	my $server = pop;
+	delete $snomask{lc $server};
+	return weechat::WEECHAT_RC_OK;
+}