]> jfr.im git - irc/SurrealServices/srsv.git/blame - branches/0.5.0/modules/serviceslibs/botserv.pm
Remove some debug lines
[irc/SurrealServices/srsv.git] / branches / 0.5.0 / modules / serviceslibs / botserv.pm
CommitLineData
aecfa1fd 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
16package botserv;
17
18use strict;
19no strict 'refs';
20
21use Safe;
026939ee 22use SrSv::IRCd::State qw($ircline synced initial_synced %IRCd_capabilities);
aecfa1fd 23use SrSv::Agent;
24use SrSv::Process::Worker 'ima_worker'; #FIXME
5e682044 25use SrSv::Insp::UUID;
aecfa1fd 26use SrSv::Text::Format qw(columnar);
27use SrSv::Errors;
5e682044 28use Data::Dumper;
aecfa1fd 29
30use SrSv::Conf2Consts qw( main services );
31
32use SrSv::User qw(get_user_nick get_user_id :flood);
33use SrSv::User::Notice;
34use SrSv::Help qw( sendhelp );
aecfa1fd 35use SrSv::ChanReg::Flags;
36use SrSv::NickReg::Flags qw(NRF_NOHIGHLIGHT nr_chk_flag_user);
026939ee 37use Data::Dumper;
aecfa1fd 38use SrSv::MySQL '$dbh';
39
40use constant {
41 F_PRIVATE => 1,
42 F_DEAF => 2
43};
44
7d4055c0 45our $bsnick_default = 'BotServ1';
aecfa1fd 46our $bsnick = $bsnick_default;
47our $botchmode;
7b3a5814 48our $bsuser = { NICK => $bsnick, ID => ircd::getAgentUuid($bsnick) };
88eba747 49
aecfa1fd 50*agent = \&chanserv::agent;
51
52our $calc_safe = new Safe;
53
54our (
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
65sub init() {
1eb006d9 66 $bsuser = { NICK => $bsnick, ID => ircd::getAgentUuid($bsnick) };
aecfa1fd 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
94sub dispatch($$$) {
7b3a5814 95 $bsuser = { NICK => $bsnick, ID => ircd::getAgentUuid($bsnick) };
5e682044 96 my ($user, $dstUser, $msg) = @_;
7b3a5814 97 $user -> {AGENT} = $bsuser;
5e682044 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);
aecfa1fd 102 }
103 elsif($dst =~ /^#/) {
104 if($msg =~ /^\!/) {
105 $has_bot->execute($dst);
106 return unless($has_bot->fetchrow_array);
92c29160 107 chan_dispatch($user, $dst, $msg);
aecfa1fd 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
122sub bs_dispatch($$$) {
5e682044 123 my ($user, $dst, $msg) = @_;
aecfa1fd 124 $msg =~ s/^\s+//;
125 my @args = split(/\s+/, $msg);
126 my $cmd = shift @args;
127
5e682044 128 $user->{AGENT} = $dst;
129 my $src = $user->{NICK};
aecfa1fd 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#
222sub 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
aecfa1fd 255 if($bot) {
256 $assign_bot->execute($bot, $cn);
1eb006d9 257 my $botUser = { NICK=>$bot, ID=>ircd::getAgentUuid ($bot) };
5e682044 258 bot_join($chan, $botUser);
aecfa1fd 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
266sub 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
286sub 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);
5e682044 319 my $bot = { NICK => $botnick};
320 get_user_id ($bot);
321 my $rsuser = { NICK => $main::rsnick };
322 get_user_id ($rsuser);
7b3a5814 323 $rsuser->{ID} = ($rsuser->{ID});
5e682044 324 ircd::setmode($rsuser, main_conf_diag, '+h', $bot);
aecfa1fd 325 notice($user, "Bot $botnick connected.");
326}
327
328sub 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
350sub 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
387sub 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
421sub 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);
5e682044 463 my $rsnick = $main::rsnick;
464 my $rsuser = { NICK => $main::rsnick };
465 get_user_id ($rsuser);
7b3a5814 466 $rsuser->{ID} = ($rsuser->{ID});
5e682044 467 my $bot = { NICK => $botnick };
468 get_user_id ($bot);
469 ircd::setmode($rsuser, main_conf_diag, '+h', $bot);
aecfa1fd 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 };
7b3a5814 476 my $botUser = { NICK=>$botnick, ID=>ircd::getAgentUuid($botnick) };
5e682044 477 bot_join($chan, $botUser)
aecfa1fd 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
493sub chan_dispatch($$$) {
5e682044 494 my ($user, $cn, $msg) = @_;
495 my $src = $user->{NICK};
aecfa1fd 496 my @args = split(/\s+/, $msg);
497 my $cmd = lc(shift @args);
498 $cmd =~ s/^\!//;
92c29160 499 my $chan = { CHAN => $cn };
500 $user->{AGENT} = agent($chan);
aecfa1fd 501 my $chan = { CHAN => $cn };
aecfa1fd 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) = @_;
5e682044 593
aecfa1fd 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
746sub 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
762sub bot_dispatch($$$) {
5e682044 763 my ($user, $bot, $msg) = @_;
aecfa1fd 764 my ($cmd, $cn, $botmsg) = split(/ /, $msg, 3);
5e682044 765 my $src = $user->{NICK};
aecfa1fd 766 my $chan = { CHAN => $cn };
aecfa1fd 767 return if flood_check($user);
aecfa1fd 768 if ($cmd =~ /^join$/i) {
5e682044 769 if (adminserv::can_do($user, 'BOT')) {
770 agent_join($bot, $cn);
771 } else {
772 notice($user, $err_deny);
773 }
aecfa1fd 774 }
775 elsif ($cmd =~ /^part$/i) {
5e682044 776 if (adminserv::can_do($user, 'BOT')) {
777 agent_part($bot, $cn, "$src requested part");
778 } else {
779 notice($user, $err_deny);
780 }
aecfa1fd 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
794sub 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
826sub chan_msg($$$) {
827 #We don't do chanmsg processing yet, like badwords.
828}
829
830sub 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);
7b3a5814 835 my $bot = { NICK => $nick, ID=>ircd::getAgentUuid($nick) };
aecfa1fd 836 agent_join($nick, main_conf_diag);
5e682044 837 my $rsuser = { NICK => $main::rsnick };
838 get_user_id ($rsuser);
5e682044 839 ircd::setmode($rsuser, main_conf_diag, '+h', $bot);
aecfa1fd 840 }
841}
842
843sub 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)) {
7b3a5814 848 my $botUser = { NICK=>$nick };
849 get_user_id ($botUser);
5e682044 850 bot_join($chan, $botUser);
aecfa1fd 851 }
852 elsif(cr_chk_flag($chan, CRF_BOTSTAY(), 1)) {
7b3a5814 853 my $botUser = { NICK=>$nick, ID=>ircd::getAgentUuid($nick) };
92c29160 854 bot_join($chan, $botUser);
aecfa1fd 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
863sub set_flag($$) {
864 my ($bot, $flag) = @_;
865
866 $set_flag->execute($flag, $bot);
867}
868
869sub unset_flag($$) {
870 my ($bot, $flag) = @_;
871
872 $unset_flag->execute($flag, $bot);
873}
874
875sub bot_join($;$) {
5e682044 876 my ($chan, $bot) = @_;
aecfa1fd 877 my $cn = $chan->{CHAN};
5e682044 878 $bot = agent($chan) unless $bot;
879 my $nick = $bot->{NICK};
aecfa1fd 880 unless(is_agent_in_chan($nick, $cn)) {
5e682044 881 agent_join($bot, $cn);
7b3a5814 882 my $bot2 = { NICK => $nick, ID => ircd::getAgentUuid($nick), UID=>ircd::getAgentUuid($nick) };
88eba747 883 if(!ircd::PREFIXAQ_DISABLE() && $IRCd_capabilities{"FOUNDER"} ne "" && $IRCd_capabilities{"ADMIN"} ne "") {
884 $botchmode = '+q';
885 } else {
886 $botchmode = '+o';
887 }
a1a0e944 888 ircd::setmode($bot2, $cn, $botchmode, $bot2 );
aecfa1fd 889 }
890}
891
892sub bot_part_if_needed($$$;$) {
47ce1466 893 my ($u, $chan, $reason, $empty) = @_;
aecfa1fd 894 my $cn = $chan->{CHAN};
895 my $bot = get_chan_bot($chan);
47ce1466 896 $u = agent($chan) unless $u;
897 my $nick = $u->{NICK};
aecfa1fd 898 return if (lc $chanserv::enforcers{lc $cn} eq lc $nick);
aecfa1fd 899 if(is_agent_in_chan($nick, $cn)) {
900 if(lc $bot eq lc $nick) {
901 if(cr_chk_flag($chan, CRF_BOTSTAY(), 1) or ($empty != 1 or chanserv::get_user_count($chan))) {
902 return;
903 }
904 }
aecfa1fd 905 agent_part($nick, $cn, $reason);
906 }
907}
908
909sub get_chan_bot($) {
910 my ($chan) = @_;
911 my $cn = $chan->{CHAN};
912 $botserv::get_chan_bot->execute($cn);
913
914 my ($bot) = $botserv::get_chan_bot->fetchrow_array();
915 $botserv::get_chan_bot->finish();
916
917 return $bot;
918}
919
9201;