html/monitor.pl
1 #!/usr/bin/perl
2 # Copyright (c) 2006 Jilles Tjoelker
3 # All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
7 # are met:
8 # 1. Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 # 2. Redistributions in binary form must reproduce the above copyright
11 # notice, this list of conditions and the following disclaimer in the
12 # documentation and/or other materials provided with the distribution.
13 #
14 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 # SUCH DAMAGE.
25
26 use strict;
27 use Irssi;
28 use vars qw($VERSION %IRSSI);
29
30 $VERSION = "1.0";
31
32 %IRSSI = (
33 authors => "Jilles Tjoelker",
34 contact => "jilles\@stack.nl",
35 name => "monitor",
36 description=> "Interface to ratbox 2.1+ /monitor command",
37 license => "BSD (revised)",
38 );
39
40 # Track the nicks in our monitor list
41 my %monitorlist;
42 # Server::connect_time we added monitor items
43 my %readded;
44 # Nicks waiting on /accept
45 my %acceptqueue;
46
47 Irssi::theme_register([monitoron => 'Now online: {nick $0} {nickhost $1}',
48 monitoroff => 'Now offline: {nick $0} {nickhost $1} {comment $2}',
49 monitorlist => 'Monitored: $0',
50 monitordel => 'No longer monitoring: $0',
51 monitoralready => 'Already monitoring: $0']);
52
53 sub event_motdgot {
54 my ($server, $args, $nick, $address) = @_;
55
56 if ($readded{$server->{tag}} != $server->{connect_time}) {
57 Irssi::print("Readding monitor items");
58 cmd_monitor_readd('', $server, undef);
59 }
60 }
61
62 sub doaccept {
63 my $server = shift;
64
65 if (defined $server->isupport("CALLERID")) {
66 $server->print('','Accepting '.$acceptqueue{$server->{tag}});
67 $server->command("QUOTE ACCEPT ".$acceptqueue{$server->{tag}});
68 } else {
69 $server->print('','Not accepting '.$acceptqueue{$server->{tag}}.', server does not support callerid');
70 }
71 $acceptqueue{$server->{tag}} = '';
72 }
73
74 sub nowonline {
75 my ($server, $nuh) = @_;
76 my ($n, $uh) = split /!/, $nuh, 2;
77 my $ln = lc $n;
78
79 $server->printformat('', MSGLEVEL_CLIENTCRAP, 'monitoron', $n, $uh);
80 $monitorlist{$server->{tag}}{$ln}{lastseen} = time();
81 if ($monitorlist{$server->{tag}}{$ln}{action} eq 'accept') {
82 if ($acceptqueue{$server->{tag}} eq '') {
83 Irssi::timeout_add_once(3000, 'doaccept', $server);
84 $acceptqueue{$server->{tag}} = $n;
85 } else {
86 $acceptqueue{$server->{tag}} .= ','.$n;
87 }
88 }
89 }
90
91 sub event_mononline {
92 my ($server, $args, $nick, $address) = @_;
93 my @a = split(/ +/, $args);
94 my ($nuh, $n, $addr, $ln);
95 # :jaguar.test 730 jilles :n!u@h,n2!u@h
96 $a[1] =~ s/^://;
97 foreach $nuh (split /,/, $a[1]) {
98 ($n, $addr) = split /!/, $nuh;
99 $ln = lc $n;
100 $monitorlist{$server->{tag}}{$ln}{address} = $addr;
101 $monitorlist{$server->{tag}}{$ln}{nick_online} = 1;
102 $monitorlist{$server->{tag}}{$ln}{mask_online} = $server->masks_match($monitorlist{$server->{tag}}{$ln}{masks}, $n, $addr);
103 if ($monitorlist{$server->{tag}}{$ln}{mask_online}) {
104 nowonline($server, $nuh);
105 } elsif ($monitorlist{$server->{tag}}{$ln}{new}) {
106 $server->printformat('', MSGLEVEL_CLIENTCRAP, 'monitoroff', $n, '');
107 }
108 $monitorlist{$server->{tag}}{$ln}{new} = 0;
109 }
110 Irssi::signal_stop();
111 }
112
113 sub event_monoffline {
114 my ($server, $args, $nick, $address) = @_;
115 my @a = split(/ +/, $args);
116 my ($n, $ln);
117 # :jaguar.test 731 jilles :n,n2
118 $a[1] =~ s/^://;
119 foreach $n (split /,/, $a[1]) {
120 $ln = lc $n;
121 if ($monitorlist{$server->{tag}}{$ln}{mask_online} || $monitorlist{$server->{tag}}{$ln}{new}) {
122 if ($monitorlist{$server->{tag}}{$ln}{nick_online}) {
123 $server->printformat('', MSGLEVEL_CLIENTCRAP, 'monitoroff', $n, $monitorlist{$server->{tag}}{$ln}{address});
124 } else {
125 $server->printformat('', MSGLEVEL_CLIENTCRAP, 'monitoroff', $n, '');
126 }
127 $monitorlist{$server->{tag}}{$ln}{lastseen} = time() if $monitorlist{$server->{tag}}{$ln}{mask_online};
128 $monitorlist{$server->{tag}}{$ln}{mask_online} = 0;
129 $monitorlist{$server->{tag}}{$ln}{new} = 0;
130 }
131 $monitorlist{$server->{tag}}{$ln}{nick_online} = 0;
132 }
133 Irssi::signal_stop();
134 }
135
136 sub event_monlist {
137 my @a = split(/ +/, $_[1]);
138 # :jaguar.test 732 jilles :n,n2
139 $a[1] =~ s/^://;
140 $a[1] =~ s/,/ /g;
141 $_[0]->printformat('', MSGLEVEL_CLIENTCRAP, 'monitorlist', $a[1]);
142 Irssi::signal_stop();
143 }
144
145 sub cmd_monitor {
146 my ($data, $server, $item) = @_;
147
148 if ($data ne '') {
149 Irssi::command_runsub ('monitor', $data, $server, $item);
150 } else {
151 cmd_monitor_show(@_);
152 }
153 }
154
155 sub cmd_monitor_add {
156 my ($data, $server, $item) = @_;
157 my @nicks;
158 my $data2;
159 my $nuh;
160 my ($n, $ln);
161 my ($doaction, $action) = (0, '');
162
163 if (!defined $server->isupport("MONITOR")) {
164 $server->print('', "No monitor support");
165 return;
166 }
167
168 if ($data =~ /-([^ ]*) (.*)/) {
169 $doaction = 1;
170 $action = $1;
171 $data = $2;
172 }
173 $data =~ s/ /,/g;
174 @nicks = split /,/, $data;
175 $data2 = '';
176 foreach $nuh (@nicks) {
177 $n = $nuh;
178 $n =~ s/!.*//;
179 next if $n eq '';
180 if ($n eq $nuh) {
181 $nuh .= '!*@*';
182 }
183 $ln = lc $n;
184 if (defined($monitorlist{$server->{tag}}{$ln})) {
185 if ($doaction) {
186 $monitorlist{$server->{tag}}{$ln}{action} = $action;
187 }
188 my $m = ' '.$monitorlist{$server->{tag}}{$ln}{masks}.' ';
189 if ($m =~ / \Q$nuh\E /) {
190 $server->printformat('', MSGLEVEL_CLIENTCRAP, 'monitoralready', $nuh);
191 next;
192 }
193 $monitorlist{$server->{tag}}{$ln}{masks} .= ' '.$nuh;
194 if ($monitorlist{$server->{tag}}{$ln}{nick_online} &&
195 !$monitorlist{$server->{tag}}{$ln}{mask_online})
196 {
197 if ($server->mask_match_address($nuh, $n, $monitorlist{$server->{tag}}{$ln}{address}))
198 {
199 $monitorlist{$server->{tag}}{$ln}{mask_online} = 1;
200 nowonline($server, $n.'!'.$monitorlist{$server->{tag}}{$ln}{address});
201 }
202 }
203 } else {
204 $data2 .= ','.$n;
205 $monitorlist{$server->{tag}}{$ln}{masks} = $nuh;
206 $monitorlist{$server->{tag}}{$ln}{nick_online} = 0;
207 $monitorlist{$server->{tag}}{$ln}{mask_online} = 0;
208 $monitorlist{$server->{tag}}{$ln}{action} = $action;
209 $monitorlist{$server->{tag}}{$ln}{lastseen} = 0;
210 $monitorlist{$server->{tag}}{$ln}{new} = 1;
211 }
212 }
213 $data2 =~ s/^,//;
214 return if ($data2 eq '');
215 $server->command("QUOTE MONITOR + $data2");
216 }
217
218 sub cmd_monitor_readd {
219 my ($data, $server, $item) = @_;
220 my ($n, $data2);
221
222 $readded{$server->{tag}} = $server->{connect_time};
223 $data2 = '';
224 foreach $n (keys %{$monitorlist{$server->{tag}}}) {
225 $data2 .= ','.$n;
226 }
227 $data2 =~ s/^,//;
228 return if ($data2 eq '');
229 if (!defined $server->isupport("MONITOR")) {
230 $server->print('', "No monitor support");
231 return;
232 }
233 $server->command("QUOTE MONITOR + $data2");
234 }
235
236 sub cmd_monitor_clear {
237 my ($data, $server, $item) = @_;
238 $monitorlist{$server->{tag}} = ();
239 $server->printformat('', MSGLEVEL_CLIENTCRAP, 'monitordel', '*');
240 if (defined $server->isupport("MONITOR")) {
241 $server->command("QUOTE MONITOR C");
242 }
243 }
244
245 sub cmd_monitor_delete {
246 my ($data, $server, $item) = @_;
247 my @nicks;
248 my $data2;
249 my $data3;
250 my $nuh;
251 my ($n, $ln);
252
253 $data =~ s/ /,/g;
254 @nicks = split /,/, $data;
255 $data2 = '';
256 $data3 = '';
257 foreach $nuh (@nicks) {
258 $n = $nuh;
259 $n =~ s/!.*//;
260 next if $n eq '';
261 $ln = lc $n;
262 next unless (defined($monitorlist{$server->{tag}}{$ln}));
263 if ($n ne $nuh) {
264 my $m = ' '.$monitorlist{$server->{tag}}{$ln}{masks}.' ';
265 next unless ($m =~ s/ \Q$nuh\E / /);
266 $m =~ s/^ //;
267 $m =~ s/ $//;
268 $monitorlist{$server->{tag}}{$ln}{masks} = $m;
269 if ($m ne '') {
270 if ($monitorlist{$server->{tag}}{$ln}{mask_online}) {
271 $monitorlist{$server->{tag}}{$ln}{mask_online} = $server->masks_match($monitorlist{$server->{tag}}{$ln}{masks}, $n, $monitorlist{$server->{tag}}{$ln}{address});
272 if (!$monitorlist{$server->{tag}}{$ln}{mask_online}) {
273 $server->printformat('', MSGLEVEL_CLIENTCRAP, 'monitoroff', $n, '');
274 }
275 }
276 $data3 .= $nuh.' ';
277 next;
278 }
279 }
280 $data3 .= $n.' ';
281 delete $monitorlist{$server->{tag}}{$ln};
282 $data2 .= ','.$n;
283 }
284 $data2 =~ s/^,//;
285 $server->printformat('', MSGLEVEL_CLIENTCRAP, 'monitordel', $data3);
286 return if ($data2 eq '');
287 #$server->printformat('', MSGLEVEL_CLIENTCRAP, 'monitordel', $data2);
288 if (defined $server->isupport("MONITOR")) {
289 $server->command("QUOTE MONITOR - $data2");
290 }
291 }
292
293 sub cmd_monitor_list {
294 my ($data, $server, $item) = @_;
295 my ($n, $ln);
296 my $count = 0;
297 my $misc;
298 my $lastseen;
299
300 foreach $n (keys %{$monitorlist{$server->{tag}}}) {
301 $ln = lc $n;
302 $misc = 'masks: '.$monitorlist{$server->{tag}}{$ln}->{masks};
303 if ($monitorlist{$server->{tag}}{$ln}->{action}) {
304 $misc .= ', action: '.$monitorlist{$server->{tag}}{$ln}->{action};
305 }
306 if ($monitorlist{$server->{tag}}{$ln}{mask_online}) {
307 $server->printformat('', MSGLEVEL_CLIENTCRAP, 'monitorlist', $n.' ('.$monitorlist{$server->{tag}}{$ln}->{address}.'), '.$misc);
308 } else {
309 if ($monitorlist{$server->{tag}}{$ln}{lastseen}) {
310 $lastseen = localtime($monitorlist{$server->{tag}}{$ln}{lastseen});
311 } else {
312 $lastseen = "never";
313 }
314 $server->printformat('', MSGLEVEL_CLIENTCRAP, 'monitorlist', $n.', '.$misc.', lastseen: '.$lastseen.', lastaddr: '.$monitorlist{$server->{tag}}{$ln}->{address});
315 }
316 $count++;
317 }
318 if ($count == 0) {
319 Irssi::print("Monitor list for ".$server->{tag}." is empty");
320 }
321 #$server->command("QUOTE MONITOR L");
322 }
323
324 sub cmd_monitor_show {
325 my ($data, $server, $item) = @_;
326 my ($n, $ln);
327 my $count = 0;
328 my $lastseen;
329
330 foreach $n (keys %{$monitorlist{$server->{tag}}}) {
331 $ln = lc $n;
332 if ($monitorlist{$server->{tag}}{$ln}{mask_online}) {
333 $server->printformat('', MSGLEVEL_CLIENTCRAP, 'monitoron', $n, $monitorlist{$server->{tag}}{$n}->{address});
334 } else {
335 if ($monitorlist{$server->{tag}}{$n}{lastseen}) {
336 $lastseen = localtime($monitorlist{$server->{tag}}{$n}{lastseen});
337 } else {
338 $lastseen = "never";
339 }
340 $server->printformat('', MSGLEVEL_CLIENTCRAP, 'monitoroff', $n, '', 'last seen: '.$lastseen);
341 }
342 $count++;
343 }
344 if ($count == 0) {
345 Irssi::print("Monitor list for ".$server->{tag}." is empty");
346 }
347 #$server->command("QUOTE MONITOR S");
348 }
349
350 sub cmd_monitor_save {
351 #my ($data, $server, $item) = @_;
352 my $file = Irssi::get_irssi_dir."/monitor";
353 my ($net, $n, $k);
354 open FILE, "> $file" or return;
355 foreach $net (keys %monitorlist) {
356 foreach $n (keys %{$monitorlist{$net}}) {
357 $monitorlist{$net}{$n}{lastseen} = time() if $monitorlist{$net}{$n}{mask_online};
358 foreach $k (keys %{$monitorlist{$net}{$n}}) {
359 next if ($k eq 'mask_online' || $k eq 'nick_online' || $k eq 'new');
360 printf FILE ("%s %s %s %s\n", $net, $n, $k, $monitorlist{$net}{$n}{$k});
361 }
362 }
363 }
364 close FILE;
365 Irssi::print("Monitor list saved to $file");
366 }
367
368 sub cmd_monitor_load {
369 #my ($data, $server, $item) = @_;
370 my $file = Irssi::get_irssi_dir."/monitor";
371
372 open FILE, "< $file" or return;
373 %monitorlist = ();
374 while (<FILE>) {
375 chomp;
376 my ($net, $n, $k, $value) = split (/ /, $_, 4);
377 $monitorlist{$net}{lc $n}{$k} = $value;
378 }
379 close FILE;
380 Irssi::print("Monitor list loaded from $file");
381 }
382
383 sub cmd_monitor_help {
384 #my ($data, $server, $item) = @_;
385
386 Irssi::print(
387 "%CNotify list using MONITOR extension%n\n".
388 "This script provides a notify list using the MONITOR extension found ".
389 "in ratbox 2.1 and newer and charybdis (MONITOR keyword in 005 numeric).\n".
390 "Each server tag has its own list.\n\n".
391 "COMMANDS:\n\n".
392 "%_/MONITOR ADD [-|-accept] nick[!user\@host]...%_\n".
393 " - Adds nicks/hostmasks or changes their accept setting, for this server. ".
394 "The nick cannot contain wildcards but the user\@host can. ".
395 "If the user\@host part is omitted, *@* is used. ".
396 "A - disables accept for the given nicks, a -accept enables it.\n".
397 "%_/MONITOR DEL nick[!user\@host]...%_\n".
398 " - Deletes nicks/hostmasks for this server. If a user\@host is given, that ".
399 "user\@host is deleted and the whole nick if it was the last, otherwise the ".
400 "whole nick.\n".
401 "%_/MONITOR CLEAR%_\n".
402 " - Clears the monitor list for this server.\n".
403 "%_/MONITOR [SHOW]%_\n".
404 " - Shows the monitor list for this server in a brief format.\n".
405 "%_/MONITOR LIST%_\n".
406 " - Shows the monitor list for this server in a long format.\n".
407 "%_/MONITOR LOAD%_\n".
408 " - Reloads the monitor list for all servers from ~/.irssi/monitor. ".
409 "The lists on the servers are not updated.\n".
410 "%_/MONITOR SAVE%_\n".
411 " - Saves the monitor list for all servers to ~/.irssi/monitor.\n".
412 "%_/MONITOR READD%_\n".
413 " - Adds the known entries to the server-side list, for this server. ".
414 "This is normally done automatically when the MOTD is received.\n".
415 "\nAfter a reload of the script it may be necessary to do /foreach server /quote monitor s.".
416 "", MSGLEVEL_CLIENTCRAP);
417 }
418
419 Irssi::signal_add('event 376', 'event_motdgot');
420 Irssi::signal_add('event 422', 'event_motdgot');
421 Irssi::signal_add('event 730', 'event_mononline');
422 Irssi::signal_add('event 731', 'event_monoffline');
423 Irssi::signal_add('event 732', 'event_monlist');
424 #Irssi::signal_add('event 733', 'event_endofmonlist');
425 #Irssi::signal_add('event 734', 'event_monlistfull');
426
427 Irssi::signal_add_last('setup saved', 'cmd_monitor_save');
428 Irssi::signal_add_last('setup reread', 'cmd_monitor_load');
429
430 Irssi::command_bind('monitor', 'cmd_monitor');
431 Irssi::command_bind('monitor add', 'cmd_monitor_add');
432 Irssi::command_bind('monitor readd', 'cmd_monitor_readd');
433 Irssi::command_bind('monitor clear', 'cmd_monitor_clear');
434 Irssi::command_bind('monitor delete', 'cmd_monitor_delete');
435 Irssi::command_bind('monitor list', 'cmd_monitor_list');
436 Irssi::command_bind('monitor show', 'cmd_monitor_show');
437 Irssi::command_bind('monitor save', 'cmd_monitor_save');
438 Irssi::command_bind('monitor load', 'cmd_monitor_load');
439 Irssi::command_bind('monitor help', 'cmd_monitor_help');
440
441 cmd_monitor_load();