1use Irssi 20020300; 2use strict; 3 4use vars qw($VERSION %IRSSI %HELP); 5$HELP{getop} = " 6GETOP [channel] 7 8Gets op on current channel or 'channel' from random opped bot added by ADDGETOP. 9"; 10$HELP{addgetop} = " 11ADDGETOP [channel] <mask> <command> 12 13Adds entry to 'channel' or current channel getop list. 14The \$0 in command specifies nick of random found mask 15in channel. 16"; 17$HELP{delgetop} = " 18DELGETOP [channel] <mask or index number from LISTGETOP> 19 20Deletes entry from getoplist on current channel or 'channel'. 21"; 22$HELP{listgetop} = " 23LISTGETOP [channel] 24 25Lists 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 37Irssi::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 46my %getop = (); 47my @userhosts; 48my $getopfile = Irssi::get_irssi_dir . "/getoplist"; 49 50sub 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 92sub 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 158sub 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 216sub 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 250sub 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 288sub 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 319sub 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 333sub 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 339sub channel_sync { 340 my $chan = shift; 341 getop_proc(lc($chan->{server}->{tag}), $chan) unless ($chan->{chanop}); 342} 343 344sub 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 359sub 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 380Irssi::command_bind( { 381 'getop' => \&sub_getop, 382 'addgetop' => \&sub_addgetop, 383 'delgetop' => \&sub_delgetop, 384 'listgetop' => \&sub_listgetop } ); 385Irssi::signal_add({ 'redir getop userhost' => \&userhost_red, 386 'event 482' => \&got_notopped, 387 'channel sync' => \&channel_sync}); 388