]>
Commit | Line | Data |
---|---|---|
7b261bb8 | 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 | ||
26 | use SrSv::Text::Format qw(columnar); | |
27 | use SrSv::Errors; | |
28 | ||
29 | use SrSv::Conf2Consts qw( main services ); | |
30 | ||
920a4abc | 31 | use SrSv::User qw(get_user_nick get_user_id :flood); |
7b261bb8 | 32 | use SrSv::User::Notice; |
33 | use SrSv::Help qw( sendhelp ); | |
34 | ||
35 | use SrSv::ChanReg::Flags; | |
36 | use SrSv::NickReg::Flags qw(NRF_NOHIGHLIGHT nr_chk_flag_user); | |
37 | ||
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 | if(!ircd::PREFIXAQ_DISABLE()) { | |
49 | $botchmode = '+q'; | |
50 | } else { | |
51 | $botchmode = '+qo'; | |
52 | } | |
53 | ||
54 | *agent = \&chanserv::agent; | |
55 | ||
56 | our $calc_safe = new Safe; | |
57 | ||
58 | our ( | |
59 | $get_all_bots, $get_botchans, $get_botstay_chans, $get_chan_bot, $get_bots_chans, $get_bot_info, | |
60 | ||
61 | $create_bot, $delete_bot, $delete_bot_allchans, $assign_bot, $unassign_bot, | |
62 | $change_bot, $update_chanreg_bot, | |
63 | ||
64 | $is_bot, $has_bot, | |
65 | ||
66 | $set_flag, $unset_flag, $get_flags | |
67 | ); | |
68 | ||
69 | sub init() { | |
70 | $get_all_bots = $dbh->prepare("SELECT nick, ident, vhost, gecos, flags FROM bot"); | |
71 | $get_botchans = $dbh->prepare("SELECT chan, COALESCE(bot, '$chanserv::csnick') FROM chanreg WHERE bot != '' OR (flags & ". CRF_BOTSTAY() . ")"); | |
72 | $get_botstay_chans = $dbh->prepare("SELECT chan, COALESCE(bot, '$chanserv::csnick') FROM chanreg WHERE (flags & ". | |
73 | CRF_BOTSTAY() . ")"); | |
74 | $get_chan_bot = $dbh->prepare("SELECT bot FROM chanreg WHERE chan=?"); | |
75 | $get_bots_chans = $dbh->prepare("SELECT chan FROM chanreg WHERE bot=?"); | |
76 | $get_bot_info = $dbh->prepare("SELECT nick, ident, vhost, gecos, flags FROM bot WHERE nick=?"); | |
77 | ||
78 | $create_bot = $dbh->prepare("INSERT INTO bot SET nick=?, ident=?, vhost=?, gecos=?"); | |
79 | $delete_bot = $dbh->prepare("DELETE FROM bot WHERE nick=?"); | |
80 | $delete_bot_allchans = $dbh->prepare("UPDATE chanreg SET bot='' WHERE bot=?"); | |
81 | $change_bot = $dbh->prepare("UPDATE bot SET nick=?, ident=?, vhost=?, gecos=? WHERE nick=?"); | |
82 | $update_chanreg_bot = $dbh->prepare("UPDATE chanreg SET bot=? WHERE bot=?"); | |
83 | ||
84 | $assign_bot = $dbh->prepare("UPDATE chanreg, bot SET chanreg.bot=bot.nick WHERE bot.nick=? AND chan=?"); | |
85 | $unassign_bot = $dbh->prepare("UPDATE chanreg SET chanreg.bot='' WHERE chan=?"); | |
86 | ||
87 | $is_bot = $dbh->prepare("SELECT 1 FROM bot WHERE nick=?"); | |
88 | $has_bot = $dbh->prepare("SELECT 1 FROM chanreg WHERE chan=? AND bot != ''"); | |
89 | ||
90 | $set_flag = $dbh->prepare("UPDATE bot SET flags=(flags | (?)) WHERE nick=?"); | |
91 | $unset_flag = $dbh->prepare("UPDATE bot SET flags=(flags & ~(?)) WHERE nick=?"); | |
92 | $get_flags = $dbh->prepare("SELECT flags FROM bot WHERE bot.nick=?"); | |
93 | ||
94 | register() unless ima_worker; #FIXME | |
95 | }; | |
96 | ||
97 | sub dispatch($$$) { | |
98 | my ($src, $dst, $msg) = @_; | |
99 | ||
100 | if(lc $dst eq lc $bsnick or lc $dst eq lc $bsnick_default ) { | |
101 | bs_dispatch($src, $dst, $msg); | |
102 | } | |
103 | elsif($dst =~ /^#/) { | |
104 | if($msg =~ /^\!/) { | |
105 | $has_bot->execute($dst); | |
106 | return unless($has_bot->fetchrow_array); | |
107 | chan_dispatch($src, $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 ($src, $dst, $msg) = @_; | |
124 | $msg =~ s/^\s+//; | |
125 | my @args = split(/\s+/, $msg); | |
126 | my $cmd = shift @args; | |
127 | ||
8a142680 | 128 | my $user = { NICK => $src, AGENT => $dst }; |
7b261bb8 | 129 | |
920a4abc | 130 | return if flood_check($user); |
7b261bb8 | 131 | |
132 | if($cmd =~ /^assign$/i) { | |
133 | if (@args == 2) { | |
134 | bs_assign($user, {CHAN => $args[0]}, $args[1]); | |
135 | } else { | |
136 | notice($user, 'Syntax: ASSIGN <#channel> <bot>'); | |
137 | } | |
138 | } | |
139 | elsif ($cmd =~ /^unassign$/i) { | |
140 | if (@args == 1) { | |
141 | bs_assign($user, {CHAN => $args[0]}, ''); | |
142 | } else { | |
143 | notice($user, 'Syntax: UNASSIGN <#channel>'); | |
144 | } | |
145 | } | |
146 | elsif ($cmd =~ /^list$/i) { | |
147 | if(@args == 0) { | |
148 | bs_list($user); | |
149 | } else { | |
150 | notice($user, 'Syntax: LIST'); | |
151 | } | |
152 | } | |
153 | elsif ($cmd =~ /^add$/i) { | |
154 | if (@args >= 4) { | |
155 | @args = split(/\s+/, $msg, 5); | |
156 | bs_add($user, $args[1], $args[2], $args[3], $args[4]); | |
157 | } else { | |
158 | notice($user, 'Syntax: ADD <nick> <ident> <vhost> <realname>'); | |
159 | } | |
160 | } | |
161 | elsif ($cmd =~ /^change$/i) { | |
162 | if (@args >= 4) { | |
163 | @args = split(/\s+/, $msg, 6); | |
164 | bs_change($user, $args[1], $args[2], $args[3], $args[4], $args[5]); | |
165 | } else { | |
166 | notice($user, 'Syntax: ADD <oldnick> <nick> <ident> <vhost> <realname>'); | |
167 | } | |
168 | } | |
169 | elsif ($cmd =~ /^del(ete)?$/i) { | |
170 | if (@args == 1) { | |
171 | bs_del($user, $args[0]); | |
172 | } else { | |
173 | notice($user, 'Syntax: DEL <botnick>'); | |
174 | } | |
175 | } | |
176 | elsif($cmd =~ /^set$/i) { | |
177 | if(@args == 3) { | |
178 | bs_set($user, $args[0], $args[1], $args[2]); | |
179 | } else { | |
180 | notice($user, 'Syntax: SET <botnick> <option> <value>'); | |
181 | } | |
182 | } | |
183 | elsif($cmd =~ /^seen$/i) { | |
184 | if(@args >= 1) { | |
185 | nickserv::ns_seen($user, @args); | |
186 | } else { | |
187 | notice($user, 'Syntax: SEEN <nick> [nick ...]'); | |
188 | } | |
189 | } | |
190 | ||
191 | elsif($cmd =~ /^(say|act)$/i) { | |
192 | if(@args > 1) { | |
193 | my @args = split(/\s+/, $msg, 3); | |
194 | my $botmsg = $args[2]; | |
195 | $botmsg = "\001ACTION $botmsg\001" if(lc $cmd eq 'act'); | |
196 | bot_say($user, {CHAN => $args[1]}, $botmsg); | |
197 | } else { | |
198 | notice($user, 'Syntax: '.uc($cmd).' <#chan> <message>'); | |
199 | } | |
200 | } | |
201 | elsif($cmd =~ /^info$/i) { | |
202 | if(@args == 1) { | |
203 | bs_info($user, $args[0]); | |
204 | } else { | |
205 | notice($user, 'Syntax: INFO <botnick>'); | |
206 | } | |
207 | } | |
208 | elsif($cmd =~ /^help$/i) { | |
209 | sendhelp($user, 'botserv', @args); | |
210 | } | |
211 | elsif($cmd =~ /^d(ice)?$/i) { | |
212 | notice($user, get_dice($args[0])); | |
213 | } | |
214 | else { | |
215 | notice($user, "Unrecognized command. For help, type: \002/bs help\002"); | |
216 | } | |
217 | } | |
218 | ||
219 | # For unassign, set $bot to '' | |
220 | # | |
221 | sub bs_assign($$$) { | |
222 | my ($user, $chan, $bot) = @_; | |
223 | ||
224 | chanserv::chk_registered($user, $chan) or return; | |
225 | ||
674d6fee | 226 | unless (chanserv::can_do($chan, 'BotAssign', $user)) { |
7b261bb8 | 227 | notice($user, $err_deny); |
228 | return; | |
229 | } | |
230 | ||
231 | if ($bot) { | |
232 | $is_bot->execute($bot); | |
233 | unless($is_bot->fetchrow_array) { | |
234 | notice($user, "\002$bot\002 is not a bot."); | |
235 | return; | |
236 | } | |
237 | } | |
238 | ||
239 | $get_flags->execute($bot); | |
240 | my ($botflags) = $get_flags->fetchrow_array; | |
241 | if (($botflags & F_PRIVATE) && !adminserv::can_do($user, 'BOT')) { | |
242 | notice($user, $err_deny); | |
243 | return; | |
244 | } | |
245 | ||
246 | ||
247 | my $cn = $chan->{CHAN}; | |
248 | my $src = get_user_nick($user); | |
249 | my $oldbot; | |
250 | if ($oldbot = get_chan_bot($chan)) { | |
251 | agent_part($oldbot, $cn, "Unassigned by \002$src\002."); | |
252 | } | |
253 | ||
254 | ||
255 | ||
256 | if($bot) { | |
257 | $assign_bot->execute($bot, $cn); | |
258 | bot_join($chan, $bot); | |
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 | ircd::setmode($main::rsnick, main_conf_diag, '+h', $botnick); | |
320 | ||
321 | notice($user, "Bot $botnick connected."); | |
322 | } | |
323 | ||
324 | sub bs_del($$) { | |
325 | my ($user, $botnick) = @_; | |
326 | ||
327 | unless (adminserv::can_do($user, 'BOT')) { | |
328 | notice($user, $err_deny); | |
329 | return; | |
330 | } | |
331 | $is_bot->execute($botnick); | |
332 | if (!$is_bot->fetchrow_array) { | |
333 | notice($user, "\002$botnick\002 is not a bot."); | |
334 | return; | |
335 | } | |
336 | ||
337 | my $src = get_user_nick($user); | |
338 | $delete_bot->execute($botnick); | |
339 | agent_quit($botnick, "Deleted by \002$src\002."); | |
340 | ircd::unsqline($botnick); | |
341 | ||
342 | $delete_bot_allchans->execute($botnick); | |
343 | notice($user, "Bot \002$botnick\002 disconnected."); | |
344 | } | |
345 | ||
346 | sub bs_set($$$$) { | |
347 | my ($user, $botnick, $set, $parm) = @_; | |
348 | ||
349 | unless (adminserv::can_do($user, 'BOT')) { | |
350 | notice($user, $err_deny); | |
351 | return; | |
352 | } | |
353 | if($set =~ /^private$/i) { | |
354 | if ($parm =~ /^(on|true)$/i) { | |
355 | set_flag($botnick, F_PRIVATE()); | |
356 | notice($user, "\002$botnick\002 is now private."); | |
357 | } | |
358 | elsif ($parm =~ /^(off|false)$/i) { | |
359 | unset_flag($botnick, F_PRIVATE()); | |
360 | notice($user, "\002$botnick\002 is now public."); | |
361 | } | |
362 | else { | |
363 | notice($user, 'Syntax: SET <botnick> PRIVATE <ON|OFF>'); | |
364 | } | |
365 | } | |
366 | if($set =~ /^deaf$/i) { | |
367 | if ($parm =~ /^(on|true)$/i) { | |
368 | set_flag($botnick, F_DEAF()); | |
369 | setagent_umode($botnick, '+d'); | |
370 | notice($user, "\002$botnick\002 is now deaf."); | |
371 | } | |
372 | elsif ($parm =~ /^(off|false)$/i) { | |
373 | unset_flag($botnick, F_DEAF()); | |
374 | setagent_umode($botnick, '-d'); | |
375 | notice($user, "\002$botnick\002 is now undeaf."); | |
376 | } | |
377 | else { | |
378 | notice($user, 'Syntax: SET <botnick> DEAF <ON|OFF>'); | |
379 | } | |
380 | } | |
381 | } | |
382 | ||
383 | sub bs_info($$) { | |
384 | my ($user, $botnick) = @_; | |
385 | ||
386 | unless (adminserv::can_do($user, 'HELP')) { | |
387 | notice($user, $err_deny); | |
388 | return; | |
389 | } | |
390 | $is_bot->execute($botnick); | |
391 | unless($is_bot->fetchrow_array) { | |
392 | notice($user, "\002$botnick\002 is not a bot."); | |
393 | return; | |
394 | } | |
395 | ||
396 | $get_bot_info->execute($botnick); | |
397 | my ($nick, $ident, $vhost, $gecos, $flags) = $get_bot_info->fetchrow_array; | |
398 | $get_bot_info->finish(); | |
399 | $get_bots_chans->execute($botnick); | |
400 | my @chans = (); | |
401 | while (my $chan = $get_bots_chans->fetchrow_array) { | |
402 | push @chans, $chan; | |
403 | } | |
404 | $get_bots_chans->finish(); | |
405 | ||
406 | notice($user, columnar({TITLE => "Information for bot \002$nick\002:", | |
407 | NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, | |
408 | ['Mask:', "$ident\@$vhost"], ['Realname:', $gecos], | |
409 | ['Flags:', (($flags & F_PRIVATE())?'Private ':'').(($flags & F_DEAF())?'Deaf ':'')], | |
410 | {COLLAPSE => [ | |
411 | 'Assigned to '. @chans.' channel(s):', | |
412 | ' ' . join(' ', @chans) | |
413 | ]} | |
414 | )); | |
415 | } | |
416 | ||
417 | sub bs_change($$$$$$) { | |
418 | my ($user, $oldnick, $botnick, $botident, $bothost, $botgecos) = @_; | |
419 | ||
420 | if (lc $oldnick eq lc $botnick) { | |
421 | notice($user, "Error: $oldnick is the same (case-insensitive) as $botnick", | |
422 | "At this time, you cannot change only the ident, host, gecos, or nick-case of a bot."); | |
423 | return; | |
424 | } | |
425 | ||
426 | unless (adminserv::can_do($user, 'BOT')) { | |
427 | notice($user, $err_deny); | |
428 | return; | |
429 | } | |
430 | ||
431 | if (my $ret = is_invalid_agentname($botnick, $botident, $bothost)) { | |
432 | notice($user, $ret); | |
433 | return; | |
434 | } | |
435 | ||
436 | if(nickserv::is_registered($botnick)) { | |
437 | notice($user, "The nick \002$botnick\002 is already registered."); | |
438 | return; | |
439 | } | |
440 | ||
441 | if(nickserv::is_online($botnick)) { | |
442 | notice($user, "The nick \002$botnick\002 is currently in use."); | |
443 | return; | |
444 | } | |
445 | ||
446 | $is_bot->execute($botnick); | |
447 | if($is_bot->fetchrow_array) { | |
448 | notice($user, "\002$botnick\002 already exists."); | |
449 | return; | |
450 | } | |
451 | ||
452 | #Create bot first, join it to its chans | |
453 | # then finally delete the old bot | |
454 | # This is to prevent races. | |
455 | $create_bot->execute($botnick, $botident, $bothost, $botgecos); | |
456 | ircd::sqline($botnick, $services::qlreason); | |
457 | agent_connect($botnick, $botident, $bothost, '+pqBSrz', $botgecos); | |
458 | agent_join($botnick, main_conf_diag); | |
459 | ircd::setmode($main::rsnick, main_conf_diag, '+h', $botnick); | |
460 | ||
461 | notice($user, "Bot $botnick connected."); | |
462 | ||
463 | $get_bots_chans->execute($oldnick); | |
464 | while(my ($cn) = $get_bots_chans->fetchrow_array()) { | |
465 | my $chan = { CHAN => $cn }; | |
466 | bot_join($chan, $botnick) | |
467 | if chanserv::get_user_count($chan) or cr_chk_flag($chan, CRF_BOTSTAY(), 1); | |
468 | } | |
469 | $get_bots_chans->finish(); | |
470 | ||
471 | $update_chanreg_bot->execute($botnick, $oldnick); $update_chanreg_bot->finish(); | |
472 | ||
473 | my $src = get_user_nick($user); | |
474 | $delete_bot->execute($oldnick); | |
475 | agent_quit($oldnick, "Deleted by \002$src\002."); | |
476 | ircd::unsqline($oldnick); | |
477 | notice($user, "Bot \002$oldnick\002 disconnected."); | |
478 | } | |
479 | ||
480 | ### CHANNEL COMMANDS ### | |
481 | ||
482 | sub chan_dispatch($$$) { | |
483 | my ($src, $cn, $msg) = @_; | |
484 | ||
485 | my @args = split(/\s+/, $msg); | |
486 | my $cmd = lc(shift @args); | |
487 | $cmd =~ s/^\!//; | |
488 | ||
489 | my $chan = { CHAN => $cn }; | |
490 | my $user = { NICK => $src, AGENT => agent($chan) }; | |
491 | ||
492 | my %cmdhash = ( | |
08fdcdbd | 493 | 'voice' => \&give_ops, |
494 | 'devoice' => \&give_ops, | |
495 | 'hop' => \&give_ops, | |
496 | 'halfop' => \&give_ops, | |
497 | 'dehop' => \&give_ops, | |
498 | 'dehalfop' => \&give_ops, | |
499 | 'op' => \&give_ops, | |
500 | 'deop' => \&give_ops, | |
501 | 'protect' => \&give_ops, | |
7b261bb8 | 502 | 'admin' => \&give_ops, |
08fdcdbd | 503 | 'deprotect' => \&give_ops, |
7b261bb8 | 504 | 'deadmin' => \&give_ops, |
505 | ||
506 | 'up' => \&up, | |
507 | ||
508 | 'down' => \&down, | |
509 | 'molest' => \&down, | |
510 | ||
511 | 'invite' => \&invite, | |
512 | ||
513 | 'kick' => \&kick, | |
514 | 'k' => \&kick, | |
515 | ||
516 | 'kb' => \&kickban, | |
517 | 'kickb' => \&kickban, | |
518 | 'kban' => \&kickban, | |
519 | 'kickban' => \&kickban, | |
520 | 'bk' => \&kickban, | |
521 | 'bkick' => \&kickban, | |
522 | 'bank' => \&kickban, | |
523 | 'bankick' => \&kickban, | |
524 | ||
525 | 'kickmask' => \&kickmask, | |
526 | 'km' => \&kickmask, | |
527 | 'kmask' => \&kickmask, | |
528 | ||
529 | 'kickbanmask' => \&kickbanmask, | |
530 | 'kickbmask' => \&kickbanmask, | |
531 | 'kickbm' => \&kickbanmask, | |
532 | 'kbm' => \&kickbanmask, | |
533 | 'kbanm' => \&kickbanmask, | |
534 | 'kbanmask' => \&kickbanmask, | |
535 | 'kbmask' => \&kickbanmask, | |
536 | ||
537 | 'calc' => \&calc, | |
538 | ||
539 | 'seen' => \&seen, | |
540 | ||
541 | #We really need something that is mostly obvious | |
542 | # and won't be used by any other bots. | |
543 | #TriviaBot I added !trivhelp | |
544 | # I guess anope uses !commands | |
545 | 'help' => \&help, | |
546 | 'commands' => \&help, | |
547 | 'botcmds' => \&help, | |
548 | ||
08fdcdbd | 549 | 'abbrevs' => \&help, |
550 | 'abbreviations' => \&help, | |
551 | 'abbrev' => \&help, | |
552 | ||
7b261bb8 | 553 | 'users' => \&alist, |
554 | 'alist' => \&alist, | |
555 | ||
556 | 'unban' => \&unban, | |
557 | ||
1aec5bc9 | 558 | 'banlist' => \&banlist, |
559 | 'blist' => \&banlist, | |
560 | ||
7b261bb8 | 561 | 'ban' => \&ban, |
562 | 'b' => \&ban, | |
563 | 'qban' => \&ban, | |
564 | 'nban' => \&ban, | |
565 | ||
566 | 'd' => \&dice, | |
567 | 'dice' => \&dice, | |
568 | ||
569 | 'mode' => \&mode, | |
570 | 'm' => \&mode, | |
571 | ||
572 | 'resync' => \&resync, | |
c5de8dcc | 573 | |
574 | 'topic' => \&topic, | |
575 | 't' => \&topic, | |
ed97ca33 | 576 | |
577 | 'why' => \&why, | |
44b46f5b | 578 | 'tempban' => \&tempban, |
579 | 'tmpban' => \&tempban, | |
580 | "tb" => \&tempban, | |
7b261bb8 | 581 | ); |
582 | ||
583 | sub give_ops { | |
696bcc83 | 584 | my ($user, $chan, $cmd, undef, @args) = @_; |
7b261bb8 | 585 | chanserv::cs_setmodes($user, $cmd, $chan, @args); |
586 | } | |
587 | sub up { | |
696bcc83 | 588 | my ($user, $chan, $cmd, undef, @args) = @_; |
7b261bb8 | 589 | chanserv::cs_updown($user, $cmd, $chan->{CHAN}, @args); |
590 | } | |
591 | sub down { | |
696bcc83 | 592 | my ($user, $chan, $cmd, undef, @args) = @_; |
7b261bb8 | 593 | if(lc $cmd eq 'molest') { |
594 | chanserv::unset_modes($user, $chan); | |
595 | } else { | |
596 | chanserv::cs_updown($user, $cmd, $chan->{CHAN}, @args); | |
597 | } | |
598 | } | |
599 | ||
600 | sub invite { | |
696bcc83 | 601 | my ($user, $chan, $cmd, undef, @args) = @_; |
7b261bb8 | 602 | chanserv::cs_invite($user, $chan, @args) unless @args == 0; |
603 | } | |
604 | ||
605 | sub kick { | |
696bcc83 | 606 | my ($user, $chan, $cmd, undef, @args) = @_; |
c1dee9a1 | 607 | my $target = shift @args or return; |
7b261bb8 | 608 | chanserv::cs_kick($user, $chan, $target, 0, join(' ', @args)); |
609 | } | |
44b46f5b | 610 | sub tempban { |
611 | my ($user, $chan, $cmd, undef, @args) = @_; | |
612 | my $target = shift @args or return; | |
613 | chanserv::cs_tempban($user, $chan, $target, join(' ', @args)); | |
614 | } | |
7b261bb8 | 615 | sub kickban { |
696bcc83 | 616 | my ($user, $chan, $cmd, undef, @args) = @_; |
c1dee9a1 | 617 | my $target = shift @args or return; |
7b261bb8 | 618 | chanserv::cs_kick($user, $chan, $target, 1, join(' ', @args)); |
619 | } | |
620 | ||
621 | sub kickmask { | |
696bcc83 | 622 | my ($user, $chan, $cmd, undef, @args) = @_; |
c1dee9a1 | 623 | my $target = shift @args or return; |
7b261bb8 | 624 | chanserv::cs_kickmask($user, $chan, $target, 0, join(' ', @args)); |
625 | } | |
626 | sub kickbanmask { | |
696bcc83 | 627 | my ($user, $chan, $cmd, undef, @args) = @_; |
c1dee9a1 | 628 | my $target = shift @args or return; |
7b261bb8 | 629 | chanserv::cs_kickmask($user, $chan, $target, 1, join(' ', @args)); |
630 | } | |
631 | ||
632 | sub calc { | |
696bcc83 | 633 | my ($user, $chan, $cmd, undef, @args) = @_; |
7b261bb8 | 634 | my $msg = join(' ', @args); |
635 | for ($msg) { | |
636 | s/,/./g; | |
637 | s/[^*.+0-9&|)(x\/^-]//g; | |
638 | s/([*+\\.\/x-])\1*/$1/g; | |
639 | s/\^/**/g; | |
640 | s/(?<!0)x//g; | |
641 | } | |
642 | ||
643 | my $answer = $calc_safe->reval("($msg) || 0"); | |
644 | $answer = 'ERROR' unless defined $answer; | |
645 | ||
646 | notice($user, ($@ ? "$msg = ERROR (${\ (split / at/, $@, 2)[0]})" : "$msg = $answer")); | |
647 | } | |
648 | ||
649 | sub seen { | |
696bcc83 | 650 | my ($user, $chan, $cmd, undef, @args) = @_; |
7b261bb8 | 651 | |
652 | if(@args >= 1) { | |
653 | nickserv::ns_seen($user, @args); | |
654 | } else { | |
655 | notice($user, 'Syntax: SEEN <nick> [nick ...]'); | |
656 | } | |
657 | } | |
658 | ||
659 | sub help { | |
696bcc83 | 660 | my ($user, $chan, $cmd, undef, @args) = @_; |
08fdcdbd | 661 | if($cmd =~ /^abbrev(iation)?s?$/) { |
662 | sendhelp($user, 'chanbot', 'abbreviations'); | |
663 | } else { | |
664 | sendhelp($user, 'chanbot'); | |
665 | } | |
7b261bb8 | 666 | } |
667 | ||
668 | sub alist { | |
696bcc83 | 669 | my ($user, $chan, $cmd, undef, @args) = @_; |
7b261bb8 | 670 | chanserv::cs_alist($user, $chan); |
671 | } | |
672 | ||
673 | sub unban { | |
696bcc83 | 674 | my ($user, $chan, $cmd, undef, @args) = @_; |
7b261bb8 | 675 | if(@args == 0) { |
676 | chanserv::cs_unban($user, $chan, get_user_nick($user)); | |
677 | } | |
678 | elsif(@args >= 1) { | |
679 | chanserv::cs_unban($user, $chan, @args); | |
680 | } | |
681 | } | |
682 | ||
683 | sub ban { | |
696bcc83 | 684 | my ($user, $chan, $cmd, undef, @args) = @_; |
7b261bb8 | 685 | $cmd =~ /^(q|n)?ban$/; my $type = $1; |
686 | if(@args >= 1) { | |
687 | chanserv::cs_ban($user, $chan, $type, @args); | |
688 | } | |
689 | } | |
690 | ||
1aec5bc9 | 691 | sub banlist { |
696bcc83 | 692 | my ($user, $chan, $cmd, undef, @args) = @_; |
1aec5bc9 | 693 | chanserv::cs_banlist($user, $chan); |
694 | } | |
695 | ||
7b261bb8 | 696 | sub dice { |
697 | # FIXME: If dice is disabled, don't count towards flooding. | |
696bcc83 | 698 | my ($user, $chan, $cmd, undef, @args) = @_; |
699 | ||
674d6fee | 700 | if(chanserv::can_do($chan, 'DICE', $user)) { |
7b261bb8 | 701 | ircd::privmsg(agent($chan), $chan->{CHAN}, |
702 | get_dice($args[0])); | |
703 | } | |
704 | } | |
705 | ||
706 | sub mode { | |
696bcc83 | 707 | my ($user, $chan, $cmd, undef, @args) = @_; |
7b261bb8 | 708 | if(@args >= 1) { |
709 | chanserv::cs_mode($user, $chan, shift @args, @args); | |
710 | } | |
711 | } | |
712 | ||
713 | sub resync { | |
696bcc83 | 714 | my ($user, $chan, $cmd) = @_; |
7b261bb8 | 715 | chanserv::cs_resync($user, $chan->{CHAN}); |
716 | } | |
717 | ||
c5de8dcc | 718 | sub topic { |
696bcc83 | 719 | my ($user, $chan, $cmd, $msg) = @_; |
c5de8dcc | 720 | if (@args >= 1) { |
696bcc83 | 721 | $msg =~ s/^!$cmd //; |
5b9e7750 | 722 | chanserv::cs_topic($user, $chan, $msg); |
c5de8dcc | 723 | } |
724 | } | |
725 | ||
bf645f27 | 726 | sub why { |
ed97ca33 | 727 | my ($user, $chan, $cmd, undef, @args) = @_; |
728 | ||
729 | if(@args >= 1) { | |
bf645f27 | 730 | chanserv::cs_why($user, $chan, @args); |
ed97ca33 | 731 | } else { |
732 | notice($user, 'Syntax: WHY <nick> [nick ...]'); | |
733 | } | |
734 | } | |
7b261bb8 | 735 | if(defined($cmdhash{$cmd})) { |
920a4abc | 736 | return if flood_check($user); |
7b261bb8 | 737 | |
696bcc83 | 738 | &{$cmdhash{$cmd}}($user, $chan, $cmd, $msg, @args); |
7b261bb8 | 739 | } |
740 | } | |
741 | ||
742 | sub bot_say($$$) { | |
743 | my ($user, $chan, $botmsg) = @_; | |
744 | my $cn = $chan->{CHAN}; | |
745 | ||
674d6fee | 746 | if(chanserv::can_do($chan, 'BotSay', $user)) { |
7b261bb8 | 747 | ircd::notice(agent($chan), '%'.$cn, get_user_nick($user).' used BotSay') |
748 | if cr_chk_flag($chan, CRF_VERBOSE()); | |
749 | ircd::privmsg(agent($chan), $cn, $botmsg); | |
750 | } else { | |
e00fcba1 | 751 | # can_do will give the $err_deny for us. |
752 | #notice($user, $err_deny); | |
7b261bb8 | 753 | } |
754 | } | |
755 | ||
756 | ### BOT COMMANDS ### | |
757 | ||
758 | sub bot_dispatch($$$) { | |
759 | my ($src, $bot, $msg) = @_; | |
760 | ||
761 | my ($cmd, $cn, $botmsg) = split(/ /, $msg, 3); | |
762 | ||
763 | my $user = { NICK => $src, AGENT => $bot }; | |
764 | my $chan = { CHAN => $cn }; | |
765 | ||
920a4abc | 766 | return if flood_check($user); |
7b261bb8 | 767 | |
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 | agent_join($nick, main_conf_diag); | |
836 | ircd::setmode($main::rsnick, main_conf_diag, '+h', $nick); | |
837 | } | |
838 | } | |
839 | ||
840 | sub eos() { | |
841 | $get_botchans->execute(); | |
842 | while(my ($cn, $nick) = $get_botchans->fetchrow_array) { | |
843 | my $chan = { CHAN => $cn }; | |
844 | if(chanserv::get_user_count($chan)) { | |
845 | bot_join($chan, $nick); | |
846 | } | |
847 | elsif(cr_chk_flag($chan, CRF_BOTSTAY(), 1)) { | |
848 | bot_join($chan, $nick); | |
849 | my $modelock = chanserv::get_modelock($chan); | |
850 | ircd::setmode(main_conf_local, $cn, $modelock) if $modelock; | |
851 | } | |
852 | } | |
853 | } | |
854 | ||
855 | ### Database Functions ### | |
856 | ||
857 | sub set_flag($$) { | |
858 | my ($bot, $flag) = @_; | |
859 | ||
860 | $set_flag->execute($flag, $bot); | |
861 | } | |
862 | ||
863 | sub unset_flag($$) { | |
864 | my ($bot, $flag) = @_; | |
865 | ||
866 | $unset_flag->execute($flag, $bot); | |
867 | } | |
868 | ||
869 | sub bot_join($;$) { | |
870 | my ($chan, $nick) = @_; | |
871 | ||
872 | my $cn = $chan->{CHAN}; | |
873 | ||
874 | $nick = agent($chan) unless $nick; | |
875 | ||
876 | unless(is_agent_in_chan($nick, $cn)) { | |
877 | agent_join($nick, $cn); | |
878 | ircd::setmode($nick, $cn, $botchmode, $nick.(ircd::PREFIXAQ_DISABLE() ? ' '.$nick : '') ); | |
879 | } | |
880 | } | |
881 | ||
882 | sub bot_part_if_needed($$$;$) { | |
883 | my ($nick, $chan, $reason, $empty) = @_; | |
884 | my $cn = $chan->{CHAN}; | |
885 | my $bot = get_chan_bot($chan); | |
886 | $nick = agent($chan) unless $nick; | |
887 | ||
888 | return if (lc $chanserv::enforcers{lc $cn} eq lc $nick); | |
889 | ||
890 | if(is_agent_in_chan($nick, $cn)) { | |
891 | if(lc $bot eq lc $nick) { | |
892 | if(cr_chk_flag($chan, CRF_BOTSTAY(), 1) or ($empty != 1 or chanserv::get_user_count($chan))) { | |
893 | return; | |
894 | } | |
895 | } | |
896 | ||
897 | agent_part($nick, $cn, $reason); | |
898 | } | |
899 | } | |
900 | ||
901 | sub get_chan_bot($) { | |
902 | my ($chan) = @_; | |
903 | my $cn = $chan->{CHAN}; | |
904 | $botserv::get_chan_bot->execute($cn); | |
905 | ||
906 | my ($bot) = $botserv::get_chan_bot->fetchrow_array(); | |
907 | $botserv::get_chan_bot->finish(); | |
908 | ||
909 | return $bot; | |
910 | } | |
911 | ||
912 | 1; |