1### operit.pl (c) 2002, 2003 Petr Baudis <pasky@ucw.cz>
2#
3## Perform certain action (invite/op/...) on request authenticated by the
4## IRC operator status.
5#
6## See http://pasky.ji.cz/~pasky/dev/irssi/ for the latest version.
7#
8## Thanks to:
9## mofo <mick@mofo.nl>			(patches)
10## Garion <garion@dnsspam.nl>		(ideas)
11## Borys <borys@irc.pl>			(ideas)
12## devastor <devastor@idiosynkrasia.net>(bug reports)
13## babar <babar@magicnet.org>		(delay patch)
14#
15## $Id: operit.pl,v 1.14 2003/09/06 12:27:11 pasky Exp pasky $
16#
17# $Log: operit.pl,v $
18# Revision 1.14  2003/09/06 12:27:11  pasky
19# Okay, so while I'm at it updated other instance of my email addy, copyright, bunch of grammar fixes and documented the operit_public_delay variable.
20#
21# Revision 1.13  2003/09/06 12:25:09  pasky
22# Updated my email addy.
23#
24# Revision 1.12  2003/09/06 12:23:50  pasky
25# Added support for randomly delayed operit - if operit is public, the delay is zero to five seconds by default - this helps greatly if there is a lot of operit-enabled clients on a channel. Patch by Babar <babar@magicnet.org> and me.
26#
27# Revision 1.11  2003/03/20 08:58:18  pasky
28# Match whole channel, not random part, when checking for deny_channels and deny_hosts. So you can deny operits at #iraq but still allow them at #iraqlive ;-). Thanks to viha for cooperation during testing.
29#
30# Revision 1.10  2002/11/29 16:51:46  pasky
31# Don't play with channels we aren't on. Fixes occassional 'can't call method command on undefined value'; thanks to devastor for a report.
32#
33# Revision 1.9  2002/10/19 13:12:34  pasky
34# Introduced operit_allow_public (by default on), which toggles accepting of public (on-channel) operit requests. Idea by Borys.
35#
36# Revision 1.8  2002/10/13 12:33:13  pasky
37# We don't care about /^operit [^!#&]/ anymore. Thanks fly to Garion for suggestion.
38#
39# Revision 1.7  2002/10/07 14:16:58  pasky
40# Added operit_show_requests bool setting (by default 1, that is same behaviour as now).
41#
42# Revision 1.6  2002/09/01 12:27:08  pasky
43# Erm, compilation fixes.
44#
45# Revision 1.5  2002/09/01 12:24:24  pasky
46# Allow specification of more channels separated by a comma in requests. Changed default value of operit_hosts_deny to something harmless. Patch by mofo <mick@mofo.nl>.
47#
48# Revision 1.4  2002/03/13 13:17:36  pasky
49# remove one debug message accidentally left there
50#
51# Revision 1.3  2002/03/12 18:02:37  pasky
52# invait actually works now
53#
54# Revision 1.2  2002/02/05 17:47:13  pasky
55# fixed many things :^). now it basically works how it should...
56#
57# Revision 1.1  2002/02/05 16:47:09  pasky
58# Initial revision
59#
60#
61###
62
63### Inspired by Operit-2.01b+enge script for ircII+ clients by
64## - Viha   (Viha@Theblah.Org)
65#  - Karzan (Kari@Theblah.Org)
66#
67## Credits also go to:
68#  - LuckyS  (lucky@binet.lv)              [bug  reports]
69#  - Fusion  (fusion@nuts.edu)             [bug  reports]
70#  - RA^v^EN (raven@sky.siol.org)          [bug  reports]
71#  - tumble  (tumble@openface.ca)          [beta testing]
72#  - koopal  (andre@nl.uu.net)             [script ideas]
73#  - pt      (primetime@wnol.net)          [script ideas]
74#  - delta   (delta@rus.uni-stuttgart.de)  [script ideas]
75#  - pht     (svobodam@irc.vsp.cz)         [bug  reports]
76#  - enge    (engerim@magicnet.org)        [modifications]
77#
78### The most recent version can always be found at
79#           http://www.vip.fi/~viha/irc/
80###
81
82use strict;
83
84use vars qw ($VERSION %IRSSI $rcsid);
85
86$rcsid = '$Id: operit.pl,v 1.14 2003/09/06 12:27:11 pasky Exp pasky $';
87($VERSION) = '$Revision: 1.14 $' =~ / (\d+\.\d+) /;
88%IRSSI = (
89          name        => 'operit',
90          authors     => 'Petr Baudis',
91          contact     => 'pasky@ucw.cz',
92          url         => 'http://pasky.ji.cz/~pasky/dev/irssi/',
93          license     => 'BSD',
94          description => 'Perform certain action (invite/op/...) on request authenticated by the IRC operator status.'
95         );
96
97
98use Irssi 20021117; # timeout_add_once
99use Irssi::Irc;
100
101
102my $queue = 0;   # already queued an operit? (when?)
103my $disp = 0;    # already displayed kind notice about already queued operit?
104my $cmd = "";    # command issued
105my $target = ""; # who issued the command
106my $chan = "";   # object of the command
107
108my $coperit = 0;
109my $cinvait = 0;
110my $cdamode = 0;
111my $mpublic = 0;
112
113
114sub event_privmsg {
115  my ($server, $data, $nick, $address) = @_;
116  my ($msgtarget, $text) = split(/ :/, $data, 2);
117
118  return if (Irssi::settings_get_bool("operit_deny"));
119
120  if ($text =~ s/^(invait|operit|damode)( .*)?$/$2/i) {
121    if (uc($msgtarget) eq uc($server->{nick})) {
122      $mpublic = 0;
123    } else {
124      return unless (Irssi::settings_get_bool("operit_allow_public"));
125      $mpublic = 1;
126    }
127
128    if (time - $queue < 10) {
129      Irssi::print "Operit currently deactivated or queued. Request ignored."
130	if (time - $disp > 20);
131      $disp = time;
132      return;
133    }
134
135    $cmd = $1; $target = $nick; $queue = 0; $disp = 0;
136
137#    if ($msgtarget eq $N or $cmd eq 'invait') {
138    if (1) {
139      ($_, $chan) = split /\s+/, $text; # oh.. oh well :)
140      my $a = 0;
141
142      $chan = $msgtarget if (not $chan and $msgtarget ne $server->{nick});
143      return unless ($chan =~ /^[#!&]/);
144
145      foreach (split /\s+/, Irssi::settings_get_str("operit_chans")) {
146        s/\./\\./;
147	s/\*/.*/g;
148	if ($chan =~ /^$_$/i) {
149	  $a++;
150	}
151      }
152
153      unless ($a) {
154	Irssi::print "Unauthorized $cmd $chan by $target (not in operit_chans)" if (Irssi::settings_get_bool("operit_show_requests"));
155	return;
156      }
157
158      foreach (split /\s+/, Irssi::settings_get_str("operit_chans_deny")) {
159        s/\./\\./;
160	s/\*/.*/g;
161	if ($chan =~ /^$_$/i) {
162	  Irssi::print "Unauthorized $cmd $chan by $target (in operit_chans_deny)" if (Irssi::settings_get_bool("operit_show_requests"));
163	  return;
164	}
165      }
166
167      foreach (split /^\s+$/, Irssi::settings_get_str("operit_hosts_deny")) {
168        s/\./\\./;
169	s/\*/.*/g;
170	if ($address =~ /$_/i) {
171	  Irssi::print "Unauthorized $cmd $chan by $target <$address> (in operit_hosts_deny)" if (Irssi::settings_get_bool("operit_show_requests"));
172	  return;
173	}
174      }
175
176      $queue = time;
177
178      $server->redirect_event("userhost", 1, $target, 0, 'redir userhost_operit_error',
179			      {"event 302" => "redir userhost_operit"});
180
181      $server->command("USERHOST $target");
182    }
183  }
184}
185
186sub event_userhost_error {
187  Irssi::print "Operit userhost on $target failed, aborting the action...";
188
189  $queue = $disp = 0;
190}
191
192
193sub event_userhost_operit {
194  my ($server, $data) = @_;
195  my ($mynick, $reply) = split(/ +/, $data);
196  my ($nick, $user, $host) = $reply =~ /^:(.*)=.(.*)@(.*)/;
197
198  unless ($nick =~ s/\*$//) {
199    Irssi::print "$target requested UNAUTHORIZED $cmd on channel $chan" if (Irssi::settings_get_bool("operit_show_requests"));
200    return;
201  }
202
203  Irssi::print "$target requested $cmd on channel $chan" if (Irssi::settings_get_bool("operit_show_requests"));
204
205  foreach my $chansplit (split(/\,/, $chan)) {
206    my $channel = $server->channel_find($chansplit);
207
208    next unless ($channel);
209
210    if (lc($cmd) eq "operit") {
211      if ($mpublic) {
212	my $precision = 10; # Delay precision (10 = 1/10s)
213	my $rdelay = int(rand(Irssi::settings_get_str("operit_public_delay") * $precision)) * 1000 / $precision;
214
215	Irssi::print "Waiting " . ($rdelay / 1000) . " seconds before executing PUBLIC $cmd for $target on $chan";
216	Irssi::timeout_add_once($rdelay + 11, sub { # XXX why + 10 ? --pasky
217			my ($target, $channel) = @{$_[0]};
218			my ($tgrec) = $channel->nick_find($target);
219        		$channel->command("op $target") unless ($tgrec and $tgrec->{'op'});
220		}, [ $target, $channel ]);
221      } else {
222        $channel->command("op $target");
223      }
224      $coperit++;
225
226    } elsif (lc($cmd) eq "invait") {
227      $server->command("invite $target $chansplit");
228      $cinvait++;
229
230    } elsif (lc($cmd) eq "damode") {
231      $server->command("notice $target mode for $chansplit is +$channel->{mode}");
232      $cdamode++;
233    }
234  }
235
236  $queue = $disp = 0;
237}
238
239
240sub event_ctcp {
241  my ($server, $data, $nick, $address, $target) = @_;
242
243  return if (Irssi::settings_get_bool("operit_deny"));
244
245  if ($data =~ /^operit/i) {
246    Irssi::print "$nick requested operit thru CTCP... no way!" if (Irssi::settings_get_bool("operit_show_requests"));
247    $server->command("NOTICE $nick ssshht!");
248    Irssi::signal_stop();
249  }
250}
251
252
253sub cmd_operit {
254  my ($data, $server, $channel) = @_;
255
256  if ($data =~ /^(usage|help)/i) {
257
258    foreach (split /\n/, <<USAGEE
259Operit:
260
261Local commands
262
263operit usage      - This help.
264operit help       - This help.
265operit status     - Display statistical information.
266
267Remote commands
268
269operit #chan      - Op the person in question on #chan.     (req. *)
270invait #chan      - Invait the person in question to #chan. (req. *)
271damode #chan      - Give the person in question the modes of #chan. (req. *)
272
273Variables
274
275operit_chans      - The channelmask operit/invait is permitted on. (* is *)
276operit_chans_deny - The channel(s) operit/invait is not permitted on. (* is *)
277operit_hosts_deny - The user\@host(s) operit/invait is not permitted from. (* is *)
278operit_deny       - Toogle this ON if you don't actually want invait/operit to function.
279operit_show_requests
280                  - Toogle this OFF if you don't want to see messages about operit requests.
281operit_allow_public
282                  - Toogle this OFF if you don't want requests written on channels to be proceeded.
283operit_public_delay
284		  - Set this to 0 if you don't want random delay between request and action.
285USAGEE
286	) {
287      Irssi::print $_;
288    }
289
290  } elsif ($data =~ /^status/i) {
291    my $ctotal = $coperit + $cinvait + $cdamode;
292
293    Irssi::print "Operit $VERSION Status:";
294    Irssi::print "The last person to request $cmd was $target [$chan].";
295    Irssi::print "This session has served $coperit op-requests, $cinvait invite-requests and $cdamode mode-requests.";
296    Irssi::print "Making a total of $ctotal succesful requests.";
297
298  } else {
299    Irssi::print "Excuse moi, sir? I guess that you want /operit usage ..?";
300  }
301}
302
303
304Irssi::command_bind("operit", "cmd_operit");
305Irssi::signal_add("redir userhost_operit", "event_userhost_operit");
306Irssi::signal_add("redir userhost_operit_error", "event_userhost_error");
307Irssi::signal_add("default ctcp msg", "event_ctcp");
308Irssi::signal_add("event privmsg", "event_privmsg");
309
310
311Irssi::settings_add_str("operit", "operit_chans", "#* !*");
312Irssi::settings_add_str("operit", "operit_chans_deny", "#ircophackers");
313Irssi::settings_add_str("operit", "operit_hosts_deny", "*!*@*.lamehost1 *lamehost2");
314Irssi::settings_add_bool("operit", "operit_deny", 0);
315Irssi::settings_add_bool("operit", "operit_show_requests", 1);
316Irssi::settings_add_bool("operit", "operit_allow_public", 1);
317Irssi::settings_add_str("operit", "operit_public_delay", 5);
318
319
320Irssi::print("Operit $VERSION loaded... see command 'operit usage'");
321