html/getop.pl


   1 use Irssi 20020300;
   2 use strict;
   3 
   4 use vars qw($VERSION %IRSSI %HELP);
   5 $HELP{getop} = "
   6 GETOP [channel]
   7 
   8 Gets op on current channel or 'channel' from random opped bot added by ADDGETOP.
   9 ";
  10 $HELP{addgetop} = "
  11 ADDGETOP [channel] <mask> <command>
  12 
  13 Adds entry to 'channel' or current channel getop list.
  14 The \$0 in command specifies nick of random found mask
  15 in channel.
  16 ";
  17 $HELP{delgetop} = "
  18 DELGETOP [channel] <mask or index number from LISTGETOP>
  19 
  20 Deletes entry from getoplist on current channel or 'channel'.
  21 ";
  22 $HELP{listgetop} = "
  23 LISTGETOP [channel]
  24 
  25 Lists all entries in getop list or just 'channel's getop list.
  26 ";
  27 $VERSION = "0.9b";
  28 %IRSSI = (
  29 	authors         => "Maciek \'fahren\' Freudenheim",
  30 	contact         => "fahren\@bochnia.pl",
  31 	name            => "GetOP",
  32 	description     => "Automatically request op from random opped person with specifed command from list after joining channel",
  33 	license         => "GNU GPLv2 or later",
  34 	changed         => "Fri Jan 10 03:54:07 CET 2003"
  35 );
  36 
  37 Irssi::theme_register([
  38 	'getop_listline', '[%W$[!-2]0%n]%| $[40]1%_: %_$2',
  39 	'getop_add', 'Added \'%_$2%_\' to getop list on channel %_$1%_ /$0/',
  40 	'getop_del', 'Deleted \'%_$2%_\' from getop list on channel %_$1%_ /$0/',
  41 	'getop_changed', 'Changed command for mask \'%_$2%_\' on channel %_$1%_ /$0/',
  42 	'getop_noone', '"%Y>>%n No one to get op from on $1 /$0/',
  43 	'getop_get', '%Y>>%n Getting op from %_$2%_ on $1 /$0/'
  44 ]);
  45 
  46 my %getop = ();
  47 my @userhosts;
  48 my $getopfile = Irssi::get_irssi_dir . "/getoplist";
  49 
  50 sub sub_getop {
  51 	my ($args, $server, $winit) = @_;
  52 
  53 	my $chan;
  54 	my ($channel) = $args =~ /^([^\s]+)/;
  55 
  56 	if ($server->ischannel($channel)) {
  57 		unless ($chan = $server->channel_find($channel)) {
  58 			Irssi::print("%R>>%n You are not on $channel.");
  59 			return;
  60 		}
  61 		$args =~ s/^[^\s]+\s?//;
  62 	} else {
  63 		unless ($winit && $winit->{type} eq "CHANNEL") {
  64 			Irssi::print("%R>>%n You don't have active channel in that window.");
  65 			return;
  66 		}
  67 		$channel = $winit->{name};
  68 		$chan = $winit;
  69 	}
  70 
  71 	if ($chan->{chanop}) {
  72 		Irssi::print("%R>>%n You are already opped on $channel.");
  73 		return;
  74 	}
  75 
  76 	$channel = lc($channel);
  77 	my $tag = lc($server->{tag});
  78 
  79 	unless ($getop{$tag}{$channel}) {
  80 		Irssi::print("%R>>%n Your getop list on channel $channel is empty. Use /ADDGETOP first.");
  81 		return;
  82 	};
  83 
  84 	unless ($getop{$tag}{$channel}) {
  85 		Irssi::print("%R>>%n Your getop list on channel $channel is empty.");
  86 		return;
  87 	}
  88 
  89 	getop_proc($tag, $chan);
  90 }
  91 
  92 sub sub_addgetop {
  93 	my ($args, $server, $winit) = @_;
  94 
  95 	my ($channel) = $args =~ /^([^\s]+)/;
  96 
  97 	if ($server->ischannel($channel)) {
  98 		$args =~ s/^[^\s]+\s?//;
  99 	} else {
 100 		unless ($winit && $winit->{type} eq "CHANNEL") {
 101 			Irssi::print("%R>>%n You don't have active channel in that window.");
 102 			return;
 103 		}
 104 		$channel = $winit->{name};
 105 	}
 106 
 107 	my ($mask, $command) = split(/ +/, $args, 2);
 108 	
 109 	unless ($command) {
 110 		Irssi::print("Usage: /ADDGETOP [channel] <mask or nickname> <command>. If you type '\$0' in command then it will be changed automatically into mask's nick.");
 111 		return;
 112 	}
 113 
 114 	my $cmdchar = Irssi::settings_get_str('cmdchars');
 115 	$command =~ s/^($cmdchar*)\^?/\1^/g;
 116 	
 117 	if (index($mask, "@") == -1) {
 118 		my ($c, $n);
 119 		if (($c = $server->channel_find($channel)) && ($n = $c->nick_find($mask))) {
 120 			$mask = $n->{host};
 121 			$mask =~ s/^[~+\-=^]/*/;
 122 		} else {
 123 			$server->redirect_event('userhost', 1, $mask, 0, undef, {
 124 					'event 302' => 'redir getop userhost',
 125 					'' => 'event empty' } );
 126 			$server->send_raw("USERHOST $mask");
 127 			my $uh = lc($mask) . " " . lc($channel) . " $command";
 128 			push @userhosts,  $uh;
 129 			return;
 130 		}
 131 	}
 132 	
 133 	$mask = "*!" . $mask if (index($mask, "!") == -1);
 134 	my $tag = lc($server->{tag});
 135 	my $channel = lc($channel);
 136 	
 137 	for my $entry (@{$getop{$tag}{$channel}}) {
 138 		if ($entry->{mask} eq $mask) {
 139 			$entry->{command} = $command;
 140 			Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'getop_changed', $tag, $channel, $mask, $command);
 141 			&savegetop;
 142 			return;
 143 		}
 144 	}
 145 	
 146 	my $gh = {
 147 		mask	=> $mask,
 148 		command	=> $command
 149 	};
 150 	
 151 	push @{$getop{$tag}{$channel}}, $gh;
 152 
 153 	Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'getop_add', $tag, $channel, $mask, $command);
 154 
 155 	&savegetop;
 156 }
 157 
 158 sub sub_delgetop {
 159 	my ($args, $server, $winit) = @_;
 160 
 161 	my ($channel) = $args =~ /^([^\s]+)/;
 162 
 163 	if ($server->ischannel($channel)) {
 164 		$args =~ s/^[^\s]+\s?//;
 165 	} else {
 166 		unless ($winit && $winit->{type} eq "CHANNEL") {
 167 			Irssi::print("%R>>%n You don't have active channel in that window.");
 168 			return;
 169 		}
 170 		$channel = $winit->{name};
 171 	}
 172 
 173 	my $tag = lc($server->{tag});
 174 	my $channel = lc($channel);
 175 
 176 	unless ($getop{$tag}{$channel}) {
 177 		Irssi::print("%R>>%n Your getop list on channel $channel is empty.");
 178 		return;
 179 	}
 180 
 181 	unless ($args) {
 182 		Irssi::print("%W>>%n Usage: /DELGETOP [channel] <mask | index from LISTGETOP>");
 183 		return;
 184 	}
 185 
 186 	my $num;
 187 	if ($args =~ /^[0-9]+$/) {
 188 		if ($args > scalar(@{$getop{$tag}{$channel}})) {
 189 			Irssi::print("%R>>%n No such entry in $channel getop list.");
 190 			return;
 191 		}
 192 		$num = $args - 1;
 193 	} else {
 194 		my $i = 0;
 195 		for my $entry (@{$getop{$tag}{$channel}}) {
 196 			$args eq $entry->{mask} and $num = $i, last;
 197 			$i++;
 198 		}
 199 	}
 200 
 201 	if (my($gh) = splice(@{$getop{$tag}{$channel}}, $num, 1)) {
 202 		Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'getop_del', $tag, $channel, $gh->{mask}, $gh->{command});
 203 		unless (scalar(@{$getop{$tag}{$channel}})) {
 204 			Irssi::print("%R>>%n No more entries in $channel getop list left.");
 205 			delete $getop{$tag}{$channel};
 206 		}
 207 		unless (keys %{$getop{$tag}}) {
 208 			Irssi::print("%R>>%n No more entries in getop list on $tag left.");	
 209 			delete $getop{$tag};
 210 		}
 211 	}
 212 
 213 	&savegetop;
 214 }
 215 
 216 sub sub_listgetop {
 217 	my ($args, $server, $winit) = @_;
 218 
 219 	my ($channel) = $args =~ /^([^\s]+)/;
 220 
 221 	if ($server->ischannel($channel)) {
 222 		my $tag = lc($server->{tag});
 223 		$channel = lc($channel);
 224 		unless ($getop{$tag}{$channel}) {
 225 			Irssi::print("%R>>%n Your getop list on channel $channel is empty.");
 226 			return;
 227 		}
 228 		my $i = 0;
 229 		Irssi::print("Getop list on $channel /$tag/:");
 230 		for my $entry (@{$getop{$tag}{$channel}}) {
 231 			Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'getop_listline', $i++, $entry->{mask}, $entry->{command});
 232 		}
 233 	} else {
 234 		unless (keys %getop) {
 235 			Irssi::print("%R>>%n Your getop list is empty. /ADDGETOP first.");
 236 			return;
 237 		}
 238 		for my $ircnet (keys %getop) {
 239 			for my $chan (keys %{$getop{$ircnet}}) {
 240 				Irssi::print("Channel: $chan /$ircnet/");
 241 				my $i = 1;
 242 				for my $entry (@{$getop{$ircnet}{$chan}}) {
 243 					Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'getop_listline', $i++, $entry->{mask}, $entry->{command});
 244 				}
 245 			}
 246 		}
 247 	}
 248 }
 249 
 250 sub userhost_red {
 251 	my ($server, $data) = @_;
 252 	$data =~ s/^[^ ]* :?//;
 253 
 254 	my $uh = shift @userhosts;
 255 	my ($nick, $chan, $command) = split(/ /, $uh, 3);
 256 
 257 	unless ($data && $data =~ /^([^=\*]*)\*?=.(.*)@(.*)/ && lc($1) eq $nick) {
 258 		Irssi::print("%R>>%n No such nickname: $nick");
 259 		return;
 260 	}
 261 
 262 	my ($user, $host) = ($2, $3);
 263 	$user =~ s/^[~+\-=^]/*/;
 264 	my $mask = "*!" . $user . "@" . $host;
 265 	my $tag = lc($server->{tag});
 266 	
 267 	for my $entry (@{$getop{$tag}{$chan}}) {
 268 		if ($entry->{mask} eq $mask) {
 269 			$entry->{command} = $command;
 270 			Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'getop_changed', $tag, $chan, $mask, $command);
 271 			&savegetop;
 272 			return;
 273 		}
 274 	}
 275 	
 276 	my $gh = {
 277 		mask => $mask,
 278 		command => $command
 279 	};
 280 
 281 	push @{$getop{$tag}{$chan}}, $gh;
 282 
 283 	Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'getop_add', $tag, $chan, $mask, $command);
 284 
 285 	&savegetop;
 286 }
 287 
 288 sub getop_proc ($$) {
 289 	my ($tag, $chan) = @_;
 290 
 291 	my $channel = lc($chan->{name});
 292 	return unless ($getop{$tag}{$channel});
 293 
 294 	my (@list, $mask);
 295 	for my $nick ($chan->nicks()) {
 296 		next unless ($nick->{op});
 297 		$mask = $nick->{nick} . "!" . $nick->{host};
 298 		for my $entry (@{$getop{$tag}{$channel}}) {
 299 			if (mask_match($mask, $entry->{mask})) {
 300 				my $lh = {
 301 					nick	=> $nick->{nick},
 302 					command	=> $entry->{command}
 303 				};
 304 				push @list, $lh;
 305 			}
 306 		}
 307 	}
 308 	
 309 	unless (@list) {
 310 		Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'getop_noone', $tag, $channel);
 311 	} else {
 312 		my $get = $list[int(rand(@list))];
 313 		$get->{command} =~ s/\$0/$get->{nick}/g;
 314 		Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'getop_get', $tag, $channel, $get->{nick}, $get->{command});	
 315 		$chan->command($get->{command});
 316 	}
 317 }
 318 
 319 sub mask_match ($$) {
 320 	my ($what, $match) = @_;
 321 
 322 	$match =~ s/\\/\\\\/g;
 323 	$match =~ s/\./\\\./g;
 324 	$match =~ s/\*/\.\*/g;
 325 	$match =~ s/\!/\\\!/g;
 326 	$match =~ s/\?/\./g;
 327 	$match =~ s/\+/\\\+/g;
 328 	$match =~ s/\^/\\\^/g;
 329 
 330 	return ($what =~ /^$match$/i);
 331 }
 332 
 333 sub got_notopped {
 334 	my ($server, $data) = @_;
 335 	my ($chan) = $data =~ /^[^\s]+\s([^\s]+)\s:/;
 336 	getop_proc(lc($server->{tag}), $server->channel_find($chan));
 337 }
 338 
 339 sub channel_sync {
 340 	my $chan = shift;
 341 	getop_proc(lc($chan->{server}->{tag}), $chan) unless ($chan->{chanop});
 342 }
 343 
 344 sub savegetop {
 345 	local *fp;
 346 	open (fp, ">$getopfile") or die "Couldn't open $getopfile for writing";
 347 
 348 	for my $ircnet (keys %getop) {
 349 		for my $chan (keys %{$getop{$ircnet}}) {
 350 			for my $entry (@{$getop{$ircnet}{$chan}}) {
 351 				print(fp "$ircnet $chan $entry->{mask} $entry->{command}\n");
 352 			}
 353 		}
 354 	}
 355 
 356 	close fp;
 357 }
 358 
 359 sub loadgetop {
 360 	%getop = ();
 361 	return unless (-e $getopfile);
 362 	local *fp;
 363 
 364 	open (fp, "<$getopfile") or die "Couldn't open $getopfile for reading";
 365 	local $/ = "\n";
 366 	
 367 	while (<fp>) {
 368 		chop;
 369 		my $gh = {};
 370 		my ($tag, $chan);
 371 		($tag, $chan, $gh->{mask}, $gh->{command}) = split(/ /, $_, 4);
 372 		push @{$getop{$tag}{$chan}}, $gh;
 373 	}
 374 	
 375 	close fp;
 376 }
 377 
 378 &loadgetop;
 379 
 380 Irssi::command_bind( {
 381 		'getop' => \&sub_getop,
 382 		'addgetop' => \&sub_addgetop,
 383 		'delgetop' => \&sub_delgetop,
 384 		'listgetop' => \&sub_listgetop } );
 385 Irssi::signal_add({ 'redir getop userhost' => \&userhost_red,
 386 		    'event 482' => \&got_notopped,
 387 	    	    'channel sync' => \&channel_sync});