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