weechat

- me personal weechat setup 🔵🟢
git clone git://git.acid.vegas/weechat.git
Log | Files | Refs | Archive | README

antifuck.pl (10433B)

      1 # Released into the Public Domain
      2 
      3 use strict;
      4 use warnings;
      5 
      6 no strict 'subs';
      7 
      8 my $SCRIPT_NAME = 'antifuck';
      9 my $SCRIPT_AUTHOR = 'The Krusty Krab <wowaname@volatile.ch>';
     10 my $SCRIPT_VERSION = '1.1';
     11 my $SCRIPT_LICENCE = 'Public domain';
     12 my $SCRIPT_DESC = 'Defend against forcejoins (e.g. from fuckyou.pl) and '.
     13 	'forceparts (e.g. from /remove)';
     14 
     15 my %OPTIONS = (
     16 	autopart => ['Whether to automatically part forcejoined channels. '.
     17 		'You can always do this manually with /antifuck part', '0'],
     18 	delay => ['Delay in milliseconds to wait before autoparting', '5000'],
     19 	forward => ['Whether to allow channel forwards (+f on freenode)', '1'],
     20 	ignore => ['Servers to ignore (e.g. for bouncers), separated by comma', ''],
     21 	nobufs => ['If 1, do not create buffers for forcejoined channels', '0'],
     22 	timeout =>
     23 		['Delay in milliseconds to wait for server to send JOIN after join',
     24 		'60000'],
     25 	);
     26 
     27 # %channels: channels we joined and received JOIN / NAMES for
     28 # %zombie: channels we joined but aren't yet in
     29 # %part: channels we were forced into and will part soon
     30 # %partbuf: buffers belonging to parted channels, we'll close these on
     31 #           /antifuck part
     32 our (%channels, %zombie, %part, %partbuf, $fuckbuf, $timeout_cb, $gc_cb);
     33 
     34 if (weechat::register($SCRIPT_NAME, $SCRIPT_AUTHOR, $SCRIPT_VERSION,
     35  $SCRIPT_LICENCE, $SCRIPT_DESC, '', '')) {
     36 	weechat::hook_command('antifuck', $SCRIPT_DESC, 'part', <<'HELP',
     37 This script defends against forced joins, such as from irssi's fuckyou.pl or
     38 from channel forwards, as well as forced parts, such as from the /remove
     39 command. You can configure certain behaviour using the options under
     40 "plugins.var.perl.antifuck.*". Configure rejoin-on-/remove with the
     41 irc.server_default.autorejoin and .autorejoin_delay commands.
     42 
     43 Running "/antifuck part" will close all forcejoined channels and part them where
     44 appropriate.
     45 HELP
     46 		'part', 'cmd_antifuck', '');
     47 	weechat::hook_signal('irc_server_connected', 'irc_connect', '');
     48 	weechat::hook_signal('irc_server_disconnected', 'irc_disconnect', '');
     49 	weechat::hook_signal('irc_channel_opened', 'buffer_opened', '');
     50 	weechat::hook_signal('buffer_closed', 'buffer_closed', '');
     51 	weechat::hook_signal('*,irc_out1_join', 'client_join', '');
     52 	weechat::hook_signal('*,irc_out1_part', 'client_part', '');
     53 	weechat::hook_signal('*,irc_raw_in_001', 'irc_001', '');
     54 	weechat::hook_signal('*,irc_raw_in_470', 'irc_470', '');
     55 	weechat::hook_modifier('irc_in_366', 'irc_366', '');
     56 	weechat::hook_modifier('irc_in_join', 'irc_join', '');
     57 	weechat::hook_modifier('irc_in_part', 'irc_part', '');
     58 
     59 	for my $option (keys %OPTIONS) {
     60 		weechat::config_set_plugin($option, $OPTIONS{$option}[1])
     61 		 unless weechat::config_is_set_plugin($option);
     62 		weechat::config_set_desc_plugin($option, $OPTIONS{$option}[0]);
     63 	}
     64 
     65 	my $iptr = weechat::infolist_get('buffer', '', '');
     66 
     67 	while (weechat::infolist_next($iptr)) {
     68 		next unless weechat::infolist_string($iptr, 'plugin_name') eq 'irc';
     69 		my $buf = weechat::infolist_pointer($iptr, 'pointer');
     70 		$channels{
     71 			lc weechat::buffer_get_string($buf, 'localvar_server')}{
     72 			lc weechat::buffer_get_string($buf, 'localvar_channel')} = 1;
     73 	}
     74 	weechat::infolist_free($iptr);
     75 }
     76 
     77 sub mynick
     78 {
     79 	my ($buf, $nick) = ($_[0], $_[1]);
     80 
     81 	return lc weechat::buffer_get_string($buf, 'localvar_nick') eq lc $nick;
     82 }
     83 
     84 sub ignored
     85 {
     86 	my $server = shift;
     87 	my $ignore_conf = lc weechat::config_get_plugin('ignore');
     88 
     89 	return $ignore_conf =~ /(^|,)$server($|,)/;
     90 }
     91 
     92 sub nobufs { weechat::config_get_plugin('nobufs') }
     93 
     94 sub ircbuf { weechat::buffer_search('irc', "(?i)".(join '.', @_)) }
     95 sub ircparse { weechat::info_get_hashtable(irc_message_parse =>
     96 	{ message => shift }) }
     97 
     98 sub servchan
     99 {
    100 	my $buf = shift;
    101 
    102 	return (lc weechat::buffer_get_string($buf, 'localvar_server'),
    103 		lc weechat::buffer_get_string($buf, 'localvar_channel'));
    104 }
    105 
    106 sub reset_gc
    107 {
    108 	weechat::unhook($gc_cb) if $gc_cb;
    109 	$gc_cb = weechat::hook_timer(weechat::config_get_plugin('timeout'), 0, 1,
    110 		'run_gc', '');
    111 }
    112 
    113 sub cmd_antifuck
    114 {
    115 	my (undef, $buffer, $args) = @_;
    116 
    117 	if ($args eq 'part') {
    118 		# TODO: we really need to spend more time here making sure we send the
    119 		# fewest PARTs possible, a la irc_join_delay
    120 		weechat::buffer_close($fuckbuf);
    121 	}
    122 
    123 	return weechat::WEECHAT_RC_OK;
    124 }
    125 
    126 sub fuckbuf_input { return weechat::WEECHAT_RC_OK; }
    127 
    128 sub fuckbuf_close
    129 {
    130 	weechat::buffer_close($_) for (keys %partbuf);
    131 	%partbuf = ();
    132 	$fuckbuf = '';
    133 
    134 	return weechat::WEECHAT_RC_OK;
    135 }
    136 
    137 sub irc_connect
    138 {
    139 	my $server = pop;
    140 	my ($autojoin) = (weechat::config_string(weechat::config_get(
    141 		"irc.server.$server.autojoin")) =~ /^([^ ]*)/);
    142 
    143 	$zombie{$server}{$_} = 1 for (split ',', lc($autojoin));
    144 
    145 	return weechat::WEECHAT_RC_OK;
    146 }
    147 
    148 sub irc_disconnect
    149 {
    150 	my $server = pop;
    151 
    152 	$server = lc $server;
    153 	delete $channels{$server};
    154 	delete $zombie{$server};
    155 	delete $part{$server};
    156 
    157 	return weechat::WEECHAT_RC_OK;
    158 }
    159 
    160 sub buffer_opened {
    161 	my $buffer = pop;
    162 	my ($server, $channel) = servchan($buffer);
    163 	return weechat::WEECHAT_RC_OK if exists $channels{$server}{$channel};
    164 	return weechat::WEECHAT_RC_OK if ignored($server);
    165 
    166 	$fuckbuf = weechat::buffer_new(
    167 		'antifuck',
    168 		'fuckbuf_input',
    169 		'',
    170 		'fuckbuf_close',
    171 		''
    172 		) unless $fuckbuf;
    173 
    174 	weechat::buffer_merge($buffer, $fuckbuf);
    175 	#return weechat::WEECHAT_RC_OK unless weechat::config_get_plugin('autopart');
    176 
    177 	$partbuf{$buffer} = 1;
    178 	return weechat::WEECHAT_RC_OK;
    179 }
    180 
    181 sub buffer_closed {
    182 	my $buffer = pop;
    183 
    184 	delete $partbuf{$buffer};
    185 	return weechat::WEECHAT_RC_OK;
    186 }
    187 
    188 sub client_join
    189 {
    190 	my (undef, $server, $channel) = (shift,
    191 		shift =~ /(.+),irc_out1_join/i,
    192 		shift =~ /^join :?([^ ]*)/i);
    193 	($server, $channel) = (lc $server, lc $channel);
    194 
    195 	reset_gc();
    196 
    197 	($_ eq '0' ? %{$channels{$server}} = () : $zombie{$server}{$_} = 1)
    198 		for (split ',', $channel);
    199 	return weechat::WEECHAT_RC_OK;
    200 }
    201 
    202 sub client_part
    203 {
    204 	my (undef, $server, $channel) = (shift,
    205 		shift =~ /(.+),irc_out1_part/i,
    206 		shift =~ /^part ([^ ]*)/i);
    207 	($server, $channel) = (lc $server, lc $channel);
    208 
    209 	delete $channels{$server}{$_} for (split ',', $channel);
    210 	return weechat::WEECHAT_RC_OK;
    211 }
    212 
    213 # RPL_WELCOME
    214 sub irc_001
    215 {
    216 	my (undef, $server, $message) = (shift,
    217 		shift =~ /(.+),irc_raw_in_001/, shift);
    218 
    219 	$server = lc $server;
    220 	return weechat::WEECHAT_RC_OK unless $message =~ / :- Welcome to ZNC -$/;
    221 
    222 	my $ignore_conf = lc weechat::config_get_plugin('ignore');
    223 	return weechat::WEECHAT_RC_OK if $ignore_conf =~ /(^|,)$server($|,)/;
    224 
    225 	weechat::config_set_plugin('ignore', "$ignore_conf,$server");
    226 
    227 	return weechat::WEECHAT_RC_OK;
    228 }
    229 
    230 sub irc_join
    231 {
    232 	my ($server, $message, $msghash) = (lc $_[2], $_[3], ircparse($_[3]));
    233 	my ($nick, $channel) = ($msghash->{nick}, lc $msghash->{channel});
    234 	my $buffer = ircbuf("$server.$channel");
    235 
    236 	return $message if exists $channels{$server}{$channel};
    237 	if (exists $zombie{$server}{$channel} || ignored($server)) {
    238 		delete $zombie{$server}{$channel};
    239 		$channels{$server}{$channel} = 1;
    240 		return $message;
    241 	}
    242 	# XXX return $message unless mynick($buffer, $nick);
    243 
    244 	$part{$server}{$channel} = 1;
    245 	$timeout_cb = weechat::hook_timer(
    246 		weechat::config_get_plugin('delay'), 0, 1, 'irc_join_delay', $buffer)
    247 		unless $timeout_cb || !weechat::config_get_plugin('autopart');
    248 
    249 	return $message unless nobufs();
    250 
    251 	$fuckbuf = weechat::buffer_new(
    252 		'antifuck',
    253 		'fuckbuf_input',
    254 		'',
    255 		'fuckbuf_close',
    256 		''
    257 		) unless $fuckbuf;
    258 	weechat::print($fuckbuf, weechat::prefix('join').
    259 		weechat::color('irc.color.message_join').
    260 		'You were forced to join '.weechat::color('chat_channel').$channel.
    261 		weechat::color('irc.color.message_join').', leaving');
    262 
    263 	return '';
    264 }
    265 
    266 # RPL_ENDOFNAMES
    267 sub irc_366
    268 {
    269 	my ($server, $message) = ($_[2], $_[3]);
    270 	my ($nick, $channel) = $message =~ /^:[^ ]* 366 ([^ ]*) ([^ ]*)/i;
    271 	my $buffer = ircbuf("$server.$channel");
    272 	($server, $channel) = (lc $server, lc $channel);
    273 
    274 	return $message if exists $channels{$server}{$channel};
    275 	return '' if nobufs();
    276 
    277 	weechat::print($buffer, weechat::prefix('network').
    278 		'Forcejoined, not syncing modes');
    279 
    280 	return '';
    281 }
    282 
    283 # ERR_LINKCHANNEL
    284 sub irc_470
    285 {
    286 	my (undef, $server, $oldchan, $newchan) = (shift,
    287 		shift =~ /(.+),irc_raw_in_470/,
    288 		shift =~ /^:[^ ]* 470 [^ ]+ ([^ ]+) ([^ ]+)/);
    289 	($server, $oldchan, $newchan) = (lc $server, lc $oldchan, lc $newchan);
    290 
    291 	delete $channels{$server}{$oldchan};
    292 	$channels{$server}{$newchan} = 1 if weechat::config_get_plugin('forward');
    293 	return weechat::WEECHAT_RC_OK;
    294 }
    295 
    296 sub irc_join_delay
    297 {
    298 	my $buffer = shift;
    299 
    300 	for my $server (keys %part) {
    301 		my $chans = '';
    302 
    303 		for my $chan (keys %{$part{$server}}) {
    304 			if (length($chans) + length($chan) > 500) {
    305 				weechat::hook_signal_send('irc_input_send',
    306 					weechat::WEECHAT_HOOK_SIGNAL_STRING,
    307 					"$server;;priority_low;;/part $chans");
    308 				$chans = '';
    309 			}
    310 
    311 			$chans .= "$chan,";
    312 		}
    313 
    314 		weechat::hook_signal_send('irc_input_send',
    315 			weechat::WEECHAT_HOOK_SIGNAL_STRING,
    316 			"$server;;priority_low;;/part $chans");
    317 	}
    318 	$timeout_cb = '';
    319 	%part = ();
    320 	return weechat::WEECHAT_RC_OK;
    321 }
    322 
    323 sub run_gc
    324 {
    325 	%zombie = ();
    326 	return weechat::WEECHAT_RC_OK;
    327 }
    328 
    329 sub irc_part
    330 {
    331 	my ($server, $message, $msghash) = ($_[2], $_[3], ircparse($_[3]));
    332 	my ($arj, $arj_delay, $arjd, $arjd_delay) = (
    333 		weechat::config_get("irc.server.$server.autorejoin"),
    334 		weechat::config_get("irc.server.$server.autorejoin_delay"),
    335 		weechat::config_get("irc.server_default.autorejoin"),
    336 		weechat::config_get("irc.server_default.autorejoin_delay")
    337 		);
    338 	return $message unless (
    339 		weechat::config_option_is_null($arj) ?
    340 		weechat::config_boolean($arjd) :
    341 		weechat::config_boolean($arj)
    342 		);
    343 
    344 	my ($nick, $channel, $reason) = ($msghash->{nick}, $msghash->{channel},
    345 		$msghash->{text});
    346 
    347 	my $buffer = ircbuf("$server.$channel");
    348 	my ($lserver, $lchannel) = (lc $server, lc $channel);
    349 
    350 	return $message unless mynick($buffer, $nick);
    351 	return $message unless exists $channels{$lserver}{$lchannel};
    352 	return $message if ignored($lserver);
    353 
    354 	weechat::print($buffer, weechat::prefix('quit').
    355 		weechat::color('irc.color.message_quit').
    356 		'You were forced to part '.weechat::color('chat_channel').$channel.
    357 		weechat::color('chat_delimiters').' ('.weechat::color('reset').
    358 		$reason.weechat::color('chat_delimiters').')'.
    359 		weechat::color('irc.color.message_quit').', rejoining');
    360 	my $delay = (
    361 		weechat::config_option_is_null($arj_delay) ?
    362 		weechat::config_integer($arjd_delay) :
    363 		weechat::config_integer($arj_delay)
    364 		);
    365 	weechat::command($buffer, ($delay ? "/wait $delay " : "").
    366 		"/join $channel");
    367 
    368 	return '';
    369 }