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});