]> jfr.im git - irc/SurrealServices/srsv.git/blob - branches/0.5.0/modules/serviceslibs/botserv.pm
Fixed some more stuff.. No more getuuid for normal users!
[irc/SurrealServices/srsv.git] / branches / 0.5.0 / modules / serviceslibs / botserv.pm
1 # This file is part of SurrealServices.
2 #
3 # SurrealServices is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
7 #
8 # SurrealServices is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with SurrealServices; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 package botserv;
17
18 use strict;
19 no strict 'refs';
20
21 use Safe;
22 use SrSv::IRCd::State qw($ircline synced initial_synced %IRCd_capabilities);
23 use SrSv::Agent;
24 use SrSv::Process::Worker 'ima_worker'; #FIXME
25 use SrSv::Insp::UUID;
26 use SrSv::Text::Format qw(columnar);
27 use SrSv::Errors;
28 use Data::Dumper;
29
30 use SrSv::Conf2Consts qw( main services );
31
32 use SrSv::User qw(get_user_nick get_user_id :flood);
33 use SrSv::User::Notice;
34 use SrSv::Help qw( sendhelp );
35 use SrSv::ChanReg::Flags;
36 use SrSv::NickReg::Flags qw(NRF_NOHIGHLIGHT nr_chk_flag_user);
37 use Data::Dumper;
38 use SrSv::MySQL '$dbh';
39
40 use constant {
41 F_PRIVATE => 1,
42 F_DEAF => 2
43 };
44
45 our $bsnick_default = 'BotServ';
46 our $bsnick = $bsnick_default;
47 our $botchmode;
48 our $bsuser = { NICK => $bsnick, ID => ircd::getAgentUuid($bsnick) };
49
50 *agent = \&chanserv::agent;
51
52 our $calc_safe = new Safe;
53
54 our (
55 $get_all_bots, $get_botchans, $get_botstay_chans, $get_chan_bot, $get_bots_chans, $get_bot_info,
56
57 $create_bot, $delete_bot, $delete_bot_allchans, $assign_bot, $unassign_bot,
58 $change_bot, $update_chanreg_bot,
59
60 $is_bot, $has_bot,
61
62 $set_flag, $unset_flag, $get_flags
63 );
64
65 sub init() {
66 $bsuser = { NICK => $bsnick, ID => ircd::getAgentUuid($bsnick) };
67 $get_all_bots = $dbh->prepare("SELECT nick, ident, vhost, gecos, flags FROM bot");
68 $get_botchans = $dbh->prepare("SELECT chan, COALESCE(bot, '$chanserv::csnick') FROM chanreg WHERE bot != '' OR (flags & ". CRF_BOTSTAY() . ")");
69 $get_botstay_chans = $dbh->prepare("SELECT chan, COALESCE(bot, '$chanserv::csnick') FROM chanreg WHERE (flags & ".
70 CRF_BOTSTAY() . ")");
71 $get_chan_bot = $dbh->prepare("SELECT bot FROM chanreg WHERE chan=?");
72 $get_bots_chans = $dbh->prepare("SELECT chan FROM chanreg WHERE bot=?");
73 $get_bot_info = $dbh->prepare("SELECT nick, ident, vhost, gecos, flags FROM bot WHERE nick=?");
74
75 $create_bot = $dbh->prepare("INSERT INTO bot SET nick=?, ident=?, vhost=?, gecos=?");
76 $delete_bot = $dbh->prepare("DELETE FROM bot WHERE nick=?");
77 $delete_bot_allchans = $dbh->prepare("UPDATE chanreg SET bot='' WHERE bot=?");
78 $change_bot = $dbh->prepare("UPDATE bot SET nick=?, ident=?, vhost=?, gecos=? WHERE nick=?");
79 $update_chanreg_bot = $dbh->prepare("UPDATE chanreg SET bot=? WHERE bot=?");
80
81 $assign_bot = $dbh->prepare("UPDATE chanreg, bot SET chanreg.bot=bot.nick WHERE bot.nick=? AND chan=?");
82 $unassign_bot = $dbh->prepare("UPDATE chanreg SET chanreg.bot='' WHERE chan=?");
83
84 $is_bot = $dbh->prepare("SELECT 1 FROM bot WHERE nick=?");
85 $has_bot = $dbh->prepare("SELECT 1 FROM chanreg WHERE chan=? AND bot != ''");
86
87 $set_flag = $dbh->prepare("UPDATE bot SET flags=(flags | (?)) WHERE nick=?");
88 $unset_flag = $dbh->prepare("UPDATE bot SET flags=(flags & ~(?)) WHERE nick=?");
89 $get_flags = $dbh->prepare("SELECT flags FROM bot WHERE bot.nick=?");
90
91 register() unless ima_worker; #FIXME
92 };
93
94 sub dispatch($$$) {
95 $bsuser = { NICK => $bsnick, ID => ircd::getAgentUuid($bsnick) };
96 my ($user, $dstUser, $msg) = @_;
97 $user -> {AGENT} = $bsuser;
98 my $src = $user->{NICK};
99 my $dst = $dstUser->{NICK};
100 if(lc $dstUser->{NICK} eq lc $bsnick or lc $dstUser->{NICK} eq lc $bsnick_default ) {
101 bs_dispatch($user, $dstUser, $msg);
102 }
103 elsif($dst =~ /^#/) {
104 if($msg =~ /^\!/) {
105 $has_bot->execute($dst);
106 return unless($has_bot->fetchrow_array);
107 chan_dispatch($user, $dst, $msg);
108 } else {
109 chan_msg($src, $dst, $msg);
110 }
111 }
112 else {
113 $is_bot->execute($dst);
114 if($is_bot->fetchrow_array) {
115 bot_dispatch($src, $dst, $msg);
116 }
117 }
118 }
119
120 ### BOTSERV COMMANDS ###
121
122 sub bs_dispatch($$$) {
123 my ($user, $dst, $msg) = @_;
124 $msg =~ s/^\s+//;
125 my @args = split(/\s+/, $msg);
126 my $cmd = shift @args;
127
128 $user->{AGENT} = $dst;
129 my $src = $user->{NICK};
130
131 return if flood_check($user);
132
133 if($cmd =~ /^assign$/i) {
134 if (@args == 2) {
135 bs_assign($user, {CHAN => $args[0]}, $args[1]);
136 } else {
137 notice($user, 'Syntax: ASSIGN <#channel> <bot>');
138 }
139 }
140 elsif ($cmd =~ /^unassign$/i) {
141 if (@args == 1) {
142 bs_assign($user, {CHAN => $args[0]}, '');
143 } else {
144 notice($user, 'Syntax: UNASSIGN <#channel>');
145 }
146 }
147 elsif ($cmd =~ /^list$/i) {
148 if(@args == 0) {
149 bs_list($user);
150 } else {
151 notice($user, 'Syntax: LIST');
152 }
153 }
154 elsif ($cmd =~ /^add$/i) {
155 if (@args >= 4) {
156 @args = split(/\s+/, $msg, 5);
157 bs_add($user, $args[1], $args[2], $args[3], $args[4]);
158 } else {
159 notice($user, 'Syntax: ADD <nick> <ident> <vhost> <realname>');
160 }
161 }
162 elsif ($cmd =~ /^change$/i) {
163 if (@args >= 4) {
164 @args = split(/\s+/, $msg, 6);
165 bs_change($user, $args[1], $args[2], $args[3], $args[4], $args[5]);
166 } else {
167 notice($user, 'Syntax: ADD <oldnick> <nick> <ident> <vhost> <realname>');
168 }
169 }
170 elsif ($cmd =~ /^del(ete)?$/i) {
171 if (@args == 1) {
172 bs_del($user, $args[0]);
173 } else {
174 notice($user, 'Syntax: DEL <botnick>');
175 }
176 }
177 elsif($cmd =~ /^set$/i) {
178 if(@args == 3) {
179 bs_set($user, $args[0], $args[1], $args[2]);
180 } else {
181 notice($user, 'Syntax: SET <botnick> <option> <value>');
182 }
183 }
184 elsif($cmd =~ /^seen$/i) {
185 if(@args >= 1) {
186 nickserv::ns_seen($user, @args);
187 } else {
188 notice($user, 'Syntax: SEEN <nick> [nick ...]');
189 }
190 }
191
192 elsif($cmd =~ /^(say|act)$/i) {
193 if(@args > 1) {
194 my @args = split(/\s+/, $msg, 3);
195 my $botmsg = $args[2];
196 $botmsg = "\001ACTION $botmsg\001" if(lc $cmd eq 'act');
197 bot_say($user, {CHAN => $args[1]}, $botmsg);
198 } else {
199 notice($user, 'Syntax: '.uc($cmd).' <#chan> <message>');
200 }
201 }
202 elsif($cmd =~ /^info$/i) {
203 if(@args == 1) {
204 bs_info($user, $args[0]);
205 } else {
206 notice($user, 'Syntax: INFO <botnick>');
207 }
208 }
209 elsif($cmd =~ /^help$/i) {
210 sendhelp($user, 'botserv', @args);
211 }
212 elsif($cmd =~ /^d(ice)?$/i) {
213 notice($user, get_dice($args[0]));
214 }
215 else {
216 notice($user, "Unrecognized command. For help, type: \002/bs help\002");
217 }
218 }
219
220 # For unassign, set $bot to ''
221 #
222 sub bs_assign($$$) {
223 my ($user, $chan, $bot) = @_;
224
225 chanserv::chk_registered($user, $chan) or return;
226
227 unless (chanserv::can_do($chan, 'BotAssign', $user)) {
228 notice($user, $err_deny);
229 return;
230 }
231
232 if ($bot) {
233 $is_bot->execute($bot);
234 unless($is_bot->fetchrow_array) {
235 notice($user, "\002$bot\002 is not a bot.");
236 return;
237 }
238 }
239
240 $get_flags->execute($bot);
241 my ($botflags) = $get_flags->fetchrow_array;
242 if (($botflags & F_PRIVATE) && !adminserv::can_do($user, 'BOT')) {
243 notice($user, $err_deny);
244 return;
245 }
246
247
248 my $cn = $chan->{CHAN};
249 my $src = get_user_nick($user);
250 my $oldbot;
251 if ($oldbot = get_chan_bot($chan)) {
252 agent_part($oldbot, $cn, "Unassigned by \002$src\002.");
253 }
254
255 if($bot) {
256 $assign_bot->execute($bot, $cn);
257 my $botUser = { NICK=>$bot, ID=>ircd::getAgentUuid ($bot) };
258 bot_join($chan, $botUser);
259 notice($user, "\002$bot\002 now assigned to \002$cn\002.");
260 } else {
261 $unassign_bot->execute($cn);
262 notice($user, "\002$oldbot\002 removed from \002$cn\002.");
263 }
264 }
265
266 sub bs_list($) {
267 my ($user) = @_;
268 my @data;
269 my $is_oper = adminserv::is_svsop($user, adminserv::S_HELP());
270
271 $get_all_bots->execute();
272 while (my ($botnick, $botident, $bothost, $botgecos, $flags) = $get_all_bots->fetchrow_array) {
273 if($is_oper) {
274 push @data, [$botnick, "($botident\@$bothost)", $botgecos,
275 (($flags & F_PRIVATE) ? "Private":"Public")];
276 } else {
277 next if($flags & F_PRIVATE);
278 push @data, [$botnick, "($botident\@$bothost)", $botgecos];
279 }
280 }
281
282 notice($user, columnar({TITLE => "The following bots are available:",
283 NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data));
284 }
285
286 sub bs_add($$$$$) {
287 my ($user, $botnick, $botident, $bothost, $botgecos) = @_;
288
289 unless (adminserv::can_do($user, 'BOT')) {
290 notice($user, $err_deny);
291 return;
292 }
293
294 if (my $ret = is_invalid_agentname($botnick, $botident, $bothost)) {
295 notice($user, $ret);
296 return;
297 }
298
299 if(nickserv::is_registered($botnick)) {
300 notice($user, "The nick \002$botnick\002 is already registered.");
301 return;
302 }
303
304 if(nickserv::is_online($botnick)) {
305 notice($user, "The nick \002$botnick\002 is currently in use.");
306 return;
307 }
308
309 $is_bot->execute($botnick);
310 if($is_bot->fetchrow_array) {
311 notice($user, "\002$botnick\002 already exists.");
312 return;
313 }
314
315 $create_bot->execute($botnick, $botident, $bothost, $botgecos);
316 ircd::sqline($botnick, $services::qlreason);
317 agent_connect($botnick, $botident, $bothost, '+pqBSrz', $botgecos);
318 agent_join($botnick, main_conf_diag);
319 my $bot = { NICK => $botnick};
320 get_user_id ($bot);
321 my $rsuser = { NICK => $main::rsnick };
322 get_user_id ($rsuser);
323 $rsuser->{ID} = ($rsuser->{ID});
324 ircd::setmode($rsuser, main_conf_diag, '+h', $bot);
325 notice($user, "Bot $botnick connected.");
326 }
327
328 sub bs_del($$) {
329 my ($user, $botnick) = @_;
330
331 unless (adminserv::can_do($user, 'BOT')) {
332 notice($user, $err_deny);
333 return;
334 }
335 $is_bot->execute($botnick);
336 if (!$is_bot->fetchrow_array) {
337 notice($user, "\002$botnick\002 is not a bot.");
338 return;
339 }
340
341 my $src = get_user_nick($user);
342 $delete_bot->execute($botnick);
343 agent_quit($botnick, "Deleted by \002$src\002.");
344 ircd::unsqline($botnick);
345
346 $delete_bot_allchans->execute($botnick);
347 notice($user, "Bot \002$botnick\002 disconnected.");
348 }
349
350 sub bs_set($$$$) {
351 my ($user, $botnick, $set, $parm) = @_;
352
353 unless (adminserv::can_do($user, 'BOT')) {
354 notice($user, $err_deny);
355 return;
356 }
357 if($set =~ /^private$/i) {
358 if ($parm =~ /^(on|true)$/i) {
359 set_flag($botnick, F_PRIVATE());
360 notice($user, "\002$botnick\002 is now private.");
361 }
362 elsif ($parm =~ /^(off|false)$/i) {
363 unset_flag($botnick, F_PRIVATE());
364 notice($user, "\002$botnick\002 is now public.");
365 }
366 else {
367 notice($user, 'Syntax: SET <botnick> PRIVATE <ON|OFF>');
368 }
369 }
370 if($set =~ /^deaf$/i) {
371 if ($parm =~ /^(on|true)$/i) {
372 set_flag($botnick, F_DEAF());
373 setagent_umode($botnick, '+d');
374 notice($user, "\002$botnick\002 is now deaf.");
375 }
376 elsif ($parm =~ /^(off|false)$/i) {
377 unset_flag($botnick, F_DEAF());
378 setagent_umode($botnick, '-d');
379 notice($user, "\002$botnick\002 is now undeaf.");
380 }
381 else {
382 notice($user, 'Syntax: SET <botnick> DEAF <ON|OFF>');
383 }
384 }
385 }
386
387 sub bs_info($$) {
388 my ($user, $botnick) = @_;
389
390 unless (adminserv::can_do($user, 'HELP')) {
391 notice($user, $err_deny);
392 return;
393 }
394 $is_bot->execute($botnick);
395 unless($is_bot->fetchrow_array) {
396 notice($user, "\002$botnick\002 is not a bot.");
397 return;
398 }
399
400 $get_bot_info->execute($botnick);
401 my ($nick, $ident, $vhost, $gecos, $flags) = $get_bot_info->fetchrow_array;
402 $get_bot_info->finish();
403 $get_bots_chans->execute($botnick);
404 my @chans = ();
405 while (my $chan = $get_bots_chans->fetchrow_array) {
406 push @chans, $chan;
407 }
408 $get_bots_chans->finish();
409
410 notice($user, columnar({TITLE => "Information for bot \002$nick\002:",
411 NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)},
412 ['Mask:', "$ident\@$vhost"], ['Realname:', $gecos],
413 ['Flags:', (($flags & F_PRIVATE())?'Private ':'').(($flags & F_DEAF())?'Deaf ':'')],
414 {COLLAPSE => [
415 'Assigned to '. @chans.' channel(s):',
416 ' ' . join(' ', @chans)
417 ]}
418 ));
419 }
420
421 sub bs_change($$$$$$) {
422 my ($user, $oldnick, $botnick, $botident, $bothost, $botgecos) = @_;
423
424 if (lc $oldnick eq lc $botnick) {
425 notice($user, "Error: $oldnick is the same (case-insensitive) as $botnick",
426 "At this time, you cannot change only the ident, host, gecos, or nick-case of a bot.");
427 return;
428 }
429
430 unless (adminserv::can_do($user, 'BOT')) {
431 notice($user, $err_deny);
432 return;
433 }
434
435 if (my $ret = is_invalid_agentname($botnick, $botident, $bothost)) {
436 notice($user, $ret);
437 return;
438 }
439
440 if(nickserv::is_registered($botnick)) {
441 notice($user, "The nick \002$botnick\002 is already registered.");
442 return;
443 }
444
445 if(nickserv::is_online($botnick)) {
446 notice($user, "The nick \002$botnick\002 is currently in use.");
447 return;
448 }
449
450 $is_bot->execute($botnick);
451 if($is_bot->fetchrow_array) {
452 notice($user, "\002$botnick\002 already exists.");
453 return;
454 }
455
456 #Create bot first, join it to its chans
457 # then finally delete the old bot
458 # This is to prevent races.
459 $create_bot->execute($botnick, $botident, $bothost, $botgecos);
460 ircd::sqline($botnick, $services::qlreason);
461 agent_connect($botnick, $botident, $bothost, '+pqBSrz', $botgecos);
462 agent_join($botnick, main_conf_diag);
463 my $rsnick = $main::rsnick;
464 my $rsuser = { NICK => $main::rsnick };
465 get_user_id ($rsuser);
466 $rsuser->{ID} = ($rsuser->{ID});
467 my $bot = { NICK => $botnick };
468 get_user_id ($bot);
469 ircd::setmode($rsuser, main_conf_diag, '+h', $bot);
470
471 notice($user, "Bot $botnick connected.");
472
473 $get_bots_chans->execute($oldnick);
474 while(my ($cn) = $get_bots_chans->fetchrow_array()) {
475 my $chan = { CHAN => $cn };
476 my $botUser = { NICK=>$botnick, ID=>ircd::getAgentUuid($botnick) };
477 bot_join($chan, $botUser)
478 if chanserv::get_user_count($chan) or cr_chk_flag($chan, CRF_BOTSTAY(), 1);
479 }
480 $get_bots_chans->finish();
481
482 $update_chanreg_bot->execute($botnick, $oldnick); $update_chanreg_bot->finish();
483
484 my $src = get_user_nick($user);
485 $delete_bot->execute($oldnick);
486 agent_quit($oldnick, "Deleted by \002$src\002.");
487 ircd::unsqline($oldnick);
488 notice($user, "Bot \002$oldnick\002 disconnected.");
489 }
490
491 ### CHANNEL COMMANDS ###
492
493 sub chan_dispatch($$$) {
494 my ($user, $cn, $msg) = @_;
495 my $src = $user->{NICK};
496 my @args = split(/\s+/, $msg);
497 my $cmd = lc(shift @args);
498 $cmd =~ s/^\!//;
499 my $chan = { CHAN => $cn };
500 $user->{AGENT} = agent($chan);
501 my $chan = { CHAN => $cn };
502
503 my %cmdhash = (
504 'voice' => \&give_ops,
505 'devoice' => \&give_ops,
506 'hop' => \&give_ops,
507 'halfop' => \&give_ops,
508 'dehop' => \&give_ops,
509 'dehalfop' => \&give_ops,
510 'op' => \&give_ops,
511 'deop' => \&give_ops,
512 'protect' => \&give_ops,
513 'admin' => \&give_ops,
514 'deprotect' => \&give_ops,
515 'deadmin' => \&give_ops,
516
517 'up' => \&up,
518
519 'down' => \&down,
520 'molest' => \&down,
521
522 'invite' => \&invite,
523
524 'kick' => \&kick,
525 'k' => \&kick,
526
527 'kb' => \&kickban,
528 'kickb' => \&kickban,
529 'kban' => \&kickban,
530 'kickban' => \&kickban,
531 'bk' => \&kickban,
532 'bkick' => \&kickban,
533 'bank' => \&kickban,
534 'bankick' => \&kickban,
535
536 'kickmask' => \&kickmask,
537 'km' => \&kickmask,
538 'kmask' => \&kickmask,
539
540 'kickbanmask' => \&kickbanmask,
541 'kickbmask' => \&kickbanmask,
542 'kickbm' => \&kickbanmask,
543 'kbm' => \&kickbanmask,
544 'kbanm' => \&kickbanmask,
545 'kbanmask' => \&kickbanmask,
546 'kbmask' => \&kickbanmask,
547
548 'calc' => \&calc,
549
550 'seen' => \&seen,
551
552 #We really need something that is mostly obvious
553 # and won't be used by any other bots.
554 #TriviaBot I added !trivhelp
555 # I guess anope uses !commands
556 'help' => \&help,
557 'commands' => \&help,
558 'botcmds' => \&help,
559
560 'abbrevs' => \&help,
561 'abbreviations' => \&help,
562 'abbrev' => \&help,
563
564 'users' => \&alist,
565 'alist' => \&alist,
566
567 'unban' => \&unban,
568
569 'banlist' => \&banlist,
570 'blist' => \&banlist,
571
572 'ban' => \&ban,
573 'b' => \&ban,
574 'qban' => \&ban,
575 'nban' => \&ban,
576
577 'd' => \&dice,
578 'dice' => \&dice,
579
580 'mode' => \&mode,
581 'm' => \&mode,
582
583 'resync' => \&resync,
584
585 'topic' => \&topic,
586 't' => \&topic,
587
588 'why' => \&why,
589 );
590
591 sub give_ops {
592 my ($user, $chan, $cmd, undef, @args) = @_;
593
594 chanserv::cs_setmodes($user, $cmd, $chan, @args);
595 }
596 sub up {
597 my ($user, $chan, $cmd, undef, @args) = @_;
598 chanserv::cs_updown($user, $cmd, $chan->{CHAN}, @args);
599 }
600 sub down {
601 my ($user, $chan, $cmd, undef, @args) = @_;
602 if(lc $cmd eq 'molest') {
603 chanserv::unset_modes($user, $chan);
604 } else {
605 chanserv::cs_updown($user, $cmd, $chan->{CHAN}, @args);
606 }
607 }
608
609 sub invite {
610 my ($user, $chan, $cmd, undef, @args) = @_;
611 chanserv::cs_invite($user, $chan, @args) unless @args == 0;
612 }
613
614 sub kick {
615 my ($user, $chan, $cmd, undef, @args) = @_;
616 my $target = shift @args or return;
617 chanserv::cs_kick($user, $chan, $target, 0, join(' ', @args));
618 }
619 sub kickban {
620 my ($user, $chan, $cmd, undef, @args) = @_;
621 my $target = shift @args or return;
622 chanserv::cs_kick($user, $chan, $target, 1, join(' ', @args));
623 }
624
625 sub kickmask {
626 my ($user, $chan, $cmd, undef, @args) = @_;
627 my $target = shift @args or return;
628 chanserv::cs_kickmask($user, $chan, $target, 0, join(' ', @args));
629 }
630 sub kickbanmask {
631 my ($user, $chan, $cmd, undef, @args) = @_;
632 my $target = shift @args or return;
633 chanserv::cs_kickmask($user, $chan, $target, 1, join(' ', @args));
634 }
635
636 sub calc {
637 my ($user, $chan, $cmd, undef, @args) = @_;
638 my $msg = join(' ', @args);
639 for ($msg) {
640 s/,/./g;
641 s/[^*.+0-9&|)(x\/^-]//g;
642 s/([*+\\.\/x-])\1*/$1/g;
643 s/\^/**/g;
644 s/(?<!0)x//g;
645 }
646
647 my $answer = $calc_safe->reval("($msg) || 0");
648 $answer = 'ERROR' unless defined $answer;
649
650 notice($user, ($@ ? "$msg = ERROR (${\ (split / at/, $@, 2)[0]})" : "$msg = $answer"));
651 }
652
653 sub seen {
654 my ($user, $chan, $cmd, undef, @args) = @_;
655
656 if(@args >= 1) {
657 nickserv::ns_seen($user, @args);
658 } else {
659 notice($user, 'Syntax: SEEN <nick> [nick ...]');
660 }
661 }
662
663 sub help {
664 my ($user, $chan, $cmd, undef, @args) = @_;
665 if($cmd =~ /^abbrev(iation)?s?$/) {
666 sendhelp($user, 'chanbot', 'abbreviations');
667 } else {
668 sendhelp($user, 'chanbot');
669 }
670 }
671
672 sub alist {
673 my ($user, $chan, $cmd, undef, @args) = @_;
674 chanserv::cs_alist($user, $chan);
675 }
676
677 sub unban {
678 my ($user, $chan, $cmd, undef, @args) = @_;
679 if(@args == 0) {
680 chanserv::cs_unban($user, $chan, get_user_nick($user));
681 }
682 elsif(@args >= 1) {
683 chanserv::cs_unban($user, $chan, @args);
684 }
685 }
686
687 sub ban {
688 my ($user, $chan, $cmd, undef, @args) = @_;
689 $cmd =~ /^(q|n)?ban$/; my $type = $1;
690 if(@args >= 1) {
691 chanserv::cs_ban($user, $chan, $type, @args);
692 }
693 }
694
695 sub banlist {
696 my ($user, $chan, $cmd, undef, @args) = @_;
697 chanserv::cs_banlist($user, $chan);
698 }
699
700 sub dice {
701 # FIXME: If dice is disabled, don't count towards flooding.
702 my ($user, $chan, $cmd, undef, @args) = @_;
703
704 if(chanserv::can_do($chan, 'DICE', $user)) {
705 ircd::privmsg(agent($chan), $chan->{CHAN},
706 get_dice($args[0]));
707 }
708 }
709
710 sub mode {
711 my ($user, $chan, $cmd, undef, @args) = @_;
712 if(@args >= 1) {
713 chanserv::cs_mode($user, $chan, shift @args, @args);
714 }
715 }
716
717 sub resync {
718 my ($user, $chan, $cmd) = @_;
719 chanserv::cs_resync($user, $chan->{CHAN});
720 }
721
722 sub topic {
723 my ($user, $chan, $cmd, $msg) = @_;
724 if (@args >= 1) {
725 $msg =~ s/^!$cmd //;
726 chanserv::cs_topic($user, $chan, $msg);
727 }
728 }
729
730 sub why {
731 my ($user, $chan, $cmd, undef, @args) = @_;
732
733 if(@args >= 1) {
734 chanserv::cs_why($user, $chan, @args);
735 } else {
736 notice($user, 'Syntax: WHY <nick> [nick ...]');
737 }
738 }
739 if(defined($cmdhash{$cmd})) {
740 return if flood_check($user);
741
742 &{$cmdhash{$cmd}}($user, $chan, $cmd, $msg, @args);
743 }
744 }
745
746 sub bot_say($$$) {
747 my ($user, $chan, $botmsg) = @_;
748 my $cn = $chan->{CHAN};
749
750 if(chanserv::can_do($chan, 'BotSay', $user)) {
751 ircd::notice(agent($chan), '%'.$cn, get_user_nick($user).' used BotSay')
752 if cr_chk_flag($chan, CRF_VERBOSE());
753 ircd::privmsg(agent($chan), $cn, $botmsg);
754 } else {
755 # can_do will give the $err_deny for us.
756 #notice($user, $err_deny);
757 }
758 }
759
760 ### BOT COMMANDS ###
761
762 sub bot_dispatch($$$) {
763 my ($user, $bot, $msg) = @_;
764 my ($cmd, $cn, $botmsg) = split(/ /, $msg, 3);
765 my $src = $user->{NICK};
766 my $chan = { CHAN => $cn };
767 return if flood_check($user);
768 if ($cmd =~ /^join$/i) {
769 if (adminserv::can_do($user, 'BOT')) {
770 agent_join($bot, $cn);
771 } else {
772 notice($user, $err_deny);
773 }
774 }
775 elsif ($cmd =~ /^part$/i) {
776 if (adminserv::can_do($user, 'BOT')) {
777 agent_part($bot, $cn, "$src requested part");
778 } else {
779 notice($user, $err_deny);
780 }
781 }
782 elsif ($cmd =~ /^say$/i) {
783 bot_say($user, $chan, $botmsg);
784 }
785 elsif ($cmd =~ /^act$/i) {
786 bot_say($user, $chan, "\001ACTION $botmsg\001");
787 }
788 elsif ($cmd =~ /^help$/i) {
789 #my @help; @help = ($cn) if $cn; push @help, split(/\s+/, $botmsg);
790 sendhelp($user, 'botpriv');
791 }
792 }
793
794 sub get_dice($) {
795 my ($count, $sides) = map int($_), ($_[0] ? split('d', $_[0]) : (1, 6));
796
797 if ($sides < 1 or $sides > 1000 or $count < 0 or $count > 100) {
798 return "Sorry, you can't have more than 100 dice, or 1000 sides, or less than 1 of either.";
799 }
800 $count = 1 if $count == 0;
801
802 my $sum = 0;
803
804 if($count == 1 or $count > 25) {
805 for(my $i = 1; $i <= $count; $i++) {
806 $sum += int(rand($sides)+1);
807 }
808
809 return "${count}d$sides: $sum";
810 }
811 else {
812 my @dice;
813
814 for(my $i = 1; $i <= $count; $i++) {
815 my $n = int(rand($sides)+1);
816 $sum += $n;
817 push @dice, $n;
818 }
819
820 return "${count}d$sides: $sum [" . join(' ', sort {$a <=> $b} @dice) . "]";
821 }
822 }
823
824 ### IRC EVENTS ###
825
826 sub chan_msg($$$) {
827 #We don't do chanmsg processing yet, like badwords.
828 }
829
830 sub register() {
831 $get_all_bots->execute();
832 while(my ($nick, $ident, $vhost, $gecos, $flags) = $get_all_bots->fetchrow_array) {
833 agent_connect($nick, $ident, $vhost, '+pqBSrz'.(($flags & F_DEAF())?'d':''), $gecos);
834 ircd::sqline($nick, $services::qlreason);
835 my $bot = { NICK => $nick, ID=>ircd::getAgentUuid($nick) };
836 agent_join($nick, main_conf_diag);
837 my $rsuser = { NICK => $main::rsnick };
838 get_user_id ($rsuser);
839 ircd::setmode($rsuser, main_conf_diag, '+h', $bot);
840 }
841 }
842
843 sub eos() {
844 $get_botchans->execute();
845 while(my ($cn, $nick) = $get_botchans->fetchrow_array) {
846 my $chan = { CHAN => $cn };
847 if(chanserv::get_user_count($chan)) {
848 my $botUser = { NICK=>$nick };
849 get_user_id ($botUser);
850 bot_join($chan, $botUser);
851 }
852 elsif(cr_chk_flag($chan, CRF_BOTSTAY(), 1)) {
853 my $botUser = { NICK=>$nick, ID=>ircd::getAgentUuid($nick) };
854 bot_join($chan, $botUser);
855 my $modelock = chanserv::get_modelock($chan);
856 ircd::setmode(main_conf_local, $cn, $modelock) if $modelock;
857 }
858 }
859 }
860
861 ### Database Functions ###
862
863 sub set_flag($$) {
864 my ($bot, $flag) = @_;
865
866 $set_flag->execute($flag, $bot);
867 }
868
869 sub unset_flag($$) {
870 my ($bot, $flag) = @_;
871
872 $unset_flag->execute($flag, $bot);
873 }
874
875 sub bot_join($;$) {
876 my ($chan, $bot) = @_;
877 my $cn = $chan->{CHAN};
878 $bot = agent($chan) unless $bot;
879 my $nick = $bot->{NICK};
880 unless(is_agent_in_chan($nick, $cn)) {
881 agent_join($bot, $cn);
882 my $bot2 = { NICK => $nick, ID => ircd::getAgentUuid($nick), UID=>ircd::getAgentUuid($nick) };
883 print "FOUNDERRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR " . $IRCd_capabilities{"FOUNDER"};
884 if(!ircd::PREFIXAQ_DISABLE() && $IRCd_capabilities{"FOUNDER"} ne "" && $IRCd_capabilities{"ADMIN"} ne "") {
885 $botchmode = '+q';
886 } else {
887 $botchmode = '+o';
888 }
889 ircd::setmode($bot2, $cn, $botchmode, $bot2 );
890 }
891 }
892
893 sub bot_part_if_needed($$$;$) {
894 my ($nick, $chan, $reason, $empty) = @_;
895 my $cn = $chan->{CHAN};
896 my $bot = get_chan_bot($chan);
897 $nick = agent($chan) unless $nick;
898 return if (lc $chanserv::enforcers{lc $cn} eq lc $nick);
899
900 if(is_agent_in_chan($nick, $cn)) {
901 if(lc $bot eq lc $nick) {
902 if(cr_chk_flag($chan, CRF_BOTSTAY(), 1) or ($empty != 1 or chanserv::get_user_count($chan))) {
903 return;
904 }
905 }
906
907 agent_part($nick, $cn, $reason);
908 }
909 }
910
911 sub get_chan_bot($) {
912 my ($chan) = @_;
913 my $cn = $chan->{CHAN};
914 $botserv::get_chan_bot->execute($cn);
915
916 my ($bot) = $botserv::get_chan_bot->fetchrow_array();
917 $botserv::get_chan_bot->finish();
918
919 return $bot;
920 }
921
922 1;