]>
jfr.im git - irc/quakenet/newserv.git/blob - qabot/qabot.c
6 #include "../nick/nick.h"
7 #include "../localuser/localuserchannel.h"
8 #include "../core/hooks.h"
9 #include "../core/schedule.h"
10 #include "../lib/array.h"
11 #include "../lib/base64.h"
12 #include "../lib/irc_string.h"
13 #include "../lib/splitline.h"
14 #include "../lib/version.h"
22 int qabot_spam_nickext
;
25 CommandTree
* qabot_commands
;
26 CommandTree
* qabot_chancommands
;
27 qab_bot
* qab_bots
= 0;
28 unsigned long qab_lastq_crc
= 0;
29 int qab_lastq_count
= 0;
30 qab_bot
* qabot_currentbot
= 0;
31 channel
* qabot_currentchan
= 0;
34 qab_startime
= time(0);
36 registerhook(HOOK_NICK_LOSTNICK
, qabot_lostnick
);
37 registerhook(HOOK_CHANNEL_PART
, qabot_channel_part
);
39 qabot_commands
= newcommandtree();
40 addcommandtotree(qabot_commands
, "showcommands", 0, 0, qabot_doshowcommands
);
41 addcommandtotree(qabot_commands
, "help", QAUFLAG_STAFF
, 1, qabot_dohelp
);
42 addcommandtotree(qabot_commands
, "hello", 0, 0, qabot_dohello
);
43 addcommandtotree(qabot_commands
, "save", QAUFLAG_ADMIN
, 0, qabot_dosave
);
44 addcommandtotree(qabot_commands
, "listbots", QAUFLAG_STAFF
, 0, qabot_dolistbots
);
45 addcommandtotree(qabot_commands
, "listusers", QAUFLAG_STAFF
, 0, qabot_dolistusers
);
46 addcommandtotree(qabot_commands
, "showbot", QAUFLAG_STAFF
, 1, qabot_doshowbot
);
47 addcommandtotree(qabot_commands
, "addbot", QAUFLAG_STAFF
, 6, qabot_doaddbot
);
48 addcommandtotree(qabot_commands
, "delbot", QAUFLAG_STAFF
, 1, qabot_dodelbot
);
49 addcommandtotree(qabot_commands
, "adduser", QAUFLAG_ADMIN
, 2, qabot_doadduser
);
50 addcommandtotree(qabot_commands
, "changelev", QAUFLAG_ADMIN
, 2, qabot_dochangelev
);
51 addcommandtotree(qabot_commands
, "deluser", QAUFLAG_ADMIN
, 1, qabot_dodeluser
);
52 addcommandtotree(qabot_commands
, "whois", QAUFLAG_STAFF
, 1, qabot_dowhois
);
53 addcommandtotree(qabot_commands
, "status", QAUFLAG_DEVELOPER
, 0, qabot_dostatus
);
55 qabot_chancommands
= newcommandtree();
56 addcommandtotree(qabot_chancommands
, "answer", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 2, qabot_dochananswer
);
57 addcommandtotree(qabot_chancommands
, "block", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 2, qabot_dochanblock
);
58 addcommandtotree(qabot_chancommands
, "clear", QAC_STAFFCHAN
, 0, qabot_dochanclear
);
59 addcommandtotree(qabot_chancommands
, "closechan", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 0, qabot_dochanclosechan
);
60 addcommandtotree(qabot_chancommands
, "config", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 2, qabot_dochanconfig
);
61 addcommandtotree(qabot_chancommands
, "help", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 1, qabot_dochanhelp
);
62 addcommandtotree(qabot_chancommands
, "listblocks", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 0, qabot_dochanlistblocks
);
63 addcommandtotree(qabot_chancommands
, "mic", QAC_STAFFCHAN
, 0, qabot_dochanmic
);
64 addcommandtotree(qabot_chancommands
, "moo", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 0, qabot_dochanmoo
);
65 addcommandtotree(qabot_chancommands
, "offtopic", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 30, qabot_dochanofftopic
);
66 addcommandtotree(qabot_chancommands
, "openchan", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 0, qabot_dochanopenchan
);
67 addcommandtotree(qabot_chancommands
, "ping", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 0, qabot_dochanping
);
68 addcommandtotree(qabot_chancommands
, "reset", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 1, qabot_dochanreset
);
69 addcommandtotree(qabot_chancommands
, "spam", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 30, qabot_dochanspam
);
70 addcommandtotree(qabot_chancommands
, "status", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 0, qabot_dochanstatus
);
71 addcommandtotree(qabot_chancommands
, "unblock", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 2, qabot_dochanunblock
);
72 addcommandtotree(qabot_chancommands
, "record", QAC_STAFFCHAN
, 1, qabot_dochanrecord
);
73 addcommandtotree(qabot_chancommands
, "play", QAC_STAFFCHAN
, 1, qabot_dochanplay
);
74 addcommandtotree(qabot_chancommands
, "continue", QAC_STAFFCHAN
, 0, qabot_dochancontinue
);
75 addcommandtotree(qabot_chancommands
, "stop", QAC_STAFFCHAN
, 0, qabot_dochanstop
);
76 addcommandtotree(qabot_chancommands
, "delete", QAC_STAFFCHAN
, 1, qabot_dochandelete
);
77 addcommandtotree(qabot_chancommands
, "list", QAC_STAFFCHAN
, 0, qabot_dochanlist
);
79 if ((qabot_nickext
= registernickext("QABOT")) == -1) {
83 if ((qabot_spam_nickext
= registernickext("QABOT_SPAM")) == -1) {
89 scheduleoneshot(time(NULL
) + 1, &qabot_createfakeuser
, NULL
);
90 scheduleoneshot(time(NULL
) + QABOT_SAVEWAIT
, &qabot_savetimer
, NULL
);
94 deregisterhook(HOOK_NICK_LOSTNICK
, qabot_lostnick
);
95 deregisterhook(HOOK_CHANNEL_PART
, qabot_channel_part
);
97 deleteschedule(0, qabot_savetimer
, NULL
);
102 qabot_delbot(qab_bots
);
104 if (qabot_nickext
!= -1)
105 releasenickext(qabot_nickext
);
107 if (qabot_spam_nickext
!= -1)
108 releasenickext(qabot_spam_nickext
);
111 deregisterlocaluser(qabot_nick
, "Module unloaded.");
116 qabot_squelchuser(qabot_users
);
118 deletecommandfromtree(qabot_commands
, "showcommands", qabot_doshowcommands
);
119 deletecommandfromtree(qabot_commands
, "help", qabot_dohelp
);
120 deletecommandfromtree(qabot_commands
, "hello", qabot_dohello
);
121 deletecommandfromtree(qabot_commands
, "save", qabot_dosave
);
122 deletecommandfromtree(qabot_commands
, "listbots", qabot_dolistbots
);
123 deletecommandfromtree(qabot_commands
, "listusers", qabot_dolistusers
);
124 deletecommandfromtree(qabot_commands
, "showbot", qabot_doshowbot
);
125 deletecommandfromtree(qabot_commands
, "addbot", qabot_doaddbot
);
126 deletecommandfromtree(qabot_commands
, "delbot", qabot_dodelbot
);
127 deletecommandfromtree(qabot_commands
, "adduser", qabot_doadduser
);
128 deletecommandfromtree(qabot_commands
, "changelev", qabot_dochangelev
);
129 deletecommandfromtree(qabot_commands
, "deluser", qabot_dodeluser
);
130 deletecommandfromtree(qabot_commands
, "whois", qabot_dowhois
);
131 deletecommandfromtree(qabot_commands
, "status", qabot_dostatus
);
132 destroycommandtree(qabot_commands
);
134 deletecommandfromtree(qabot_chancommands
, "answer", qabot_dochananswer
);
135 deletecommandfromtree(qabot_chancommands
, "block", qabot_dochanblock
);
136 deletecommandfromtree(qabot_chancommands
, "clear", qabot_dochanclear
);
137 deletecommandfromtree(qabot_chancommands
, "closechan", qabot_dochanclosechan
);
138 deletecommandfromtree(qabot_chancommands
, "config", qabot_dochanconfig
);
139 deletecommandfromtree(qabot_chancommands
, "help", qabot_dochanhelp
);
140 deletecommandfromtree(qabot_chancommands
, "listblocks", qabot_dochanlistblocks
);
141 deletecommandfromtree(qabot_chancommands
, "mic", qabot_dochanmic
);
142 deletecommandfromtree(qabot_chancommands
, "moo", qabot_dochanmoo
);
143 deletecommandfromtree(qabot_chancommands
, "offtopic", qabot_dochanofftopic
);
144 deletecommandfromtree(qabot_chancommands
, "openchan", qabot_dochanopenchan
);
145 deletecommandfromtree(qabot_chancommands
, "ping", qabot_dochanping
);
146 deletecommandfromtree(qabot_chancommands
, "reset", qabot_dochanreset
);
147 deletecommandfromtree(qabot_chancommands
, "spam", qabot_dochanspam
);
148 deletecommandfromtree(qabot_chancommands
, "status", qabot_dochanstatus
);
149 deletecommandfromtree(qabot_chancommands
, "unblock", qabot_dochanunblock
);
150 deletecommandfromtree(qabot_chancommands
, "record", qabot_dochanrecord
);
151 deletecommandfromtree(qabot_chancommands
, "play", qabot_dochanplay
);
152 deletecommandfromtree(qabot_chancommands
, "continue", qabot_dochancontinue
);
153 deletecommandfromtree(qabot_chancommands
, "stop", qabot_dochanstop
);
154 deletecommandfromtree(qabot_chancommands
, "delete", qabot_dochandelete
);
155 deletecommandfromtree(qabot_chancommands
, "list", qabot_dochanlist
);
156 destroycommandtree(qabot_chancommands
);
159 void qabot_lostnick(int hooknum
, void* arg
) {
160 nick
* np
= (nick
*)arg
;
169 for (b
= qab_bots
; b
; b
= b
->next
) {
170 if (b
->micnumeric
== np
->numeric
) {
172 sendmessagetochannel(b
->np
, b
->staff_chan
->channel
, "Mic deactivated.");
174 qabot_spamstored((void*)b
);
177 if (b
->recnumeric
== np
->numeric
) {
185 sendmessagetochannel(b
->np
, b
->staff_chan
->channel
, "Recorder deactivated.");
190 void qabot_channel_part(int hooknum
, void* arg
) {
191 channel
* cp
= (channel
*)((void**)arg
)[0];
192 nick
* np
= (nick
*)((void**)arg
)[1];
201 for (b
= qab_bots
; b
; b
= b
->next
) {
202 if ((b
->micnumeric
== np
->numeric
) && (b
->staff_chan
->channel
== cp
)) {
204 sendmessagetochannel(b
->np
, b
->staff_chan
->channel
, "Mic deactivated.");
206 qabot_spamstored((void*)b
);
209 if (b
->recnumeric
== np
->numeric
) {
217 sendmessagetochannel(b
->np
, b
->staff_chan
->channel
, "Recorder deactivated.");
222 void qabot_createfakeuser(void* arg
) {
225 if ((qabot_nick
= registerlocaluser(QABOT_NICK
, QABOT_USER
, QABOT_HOST
,
226 QABOT_NAME
, QABOT_ACCT
, QABOT_UMDE
, &qabot_handler
))) {
227 if ((cp
= findchannel(QABOT_HOMECHAN
))) {
228 localjoinchannel(qabot_nick
, cp
);
229 localgetops(qabot_nick
, cp
);
232 localcreatechannel(qabot_nick
, QABOT_HOMECHAN
);
236 void qabot_handler(nick
* me
, int type
, void** args
) {
247 sender
= (nick
*)args
[0];
249 if (!strncmp("\001VERSION", args
[1], 8)) {
250 sendnoticetouser(qabot_nick
, sender
, "\001VERSION %s\001", QABOT_NAME
);
254 cargc
= splitline((char*)args
[1], cargv
, 50, 0);
255 cmd
= findcommandintree(qabot_commands
, cargv
[0], 1);
257 sendnoticetouser(qabot_nick
, sender
, "Unknown command.");
261 if (!IsAccount(sender
)) {
262 sendnoticetouser(qabot_nick
, sender
, "You must be authed to use this command.");
266 u
= qabot_getuser(sender
->authname
);
269 sendnoticetouser(qabot_nick
, sender
, "You need an account to use this command.");
273 if ((cmd
->level
& QAUFLAG_STAFF
) && !QAIsStaff(u
)) {
274 sendnoticetouser(qabot_nick
, sender
, "You do not have access to this command.");
278 if ((cmd
->level
& QAUFLAG_ADMIN
) && !QAIsAdmin(u
)) {
279 sendnoticetouser(qabot_nick
, sender
, "You do not have access to this command.");
283 if ((cmd
->level
& QAUFLAG_DEVELOPER
) && !QAIsDeveloper(u
)) {
284 sendnoticetouser(qabot_nick
, sender
, "You do not have access to this command.");
289 if (cmd
->maxparams
< (cargc
-1)) {
290 rejoinline(cargv
[cmd
->maxparams
], cargc
- (cmd
->maxparams
));
291 cargc
=(cmd
->maxparams
) + 1;
294 (cmd
->handler
)((void*)sender
, cargc
- 1, &(cargv
[1]));
300 scheduleoneshot(time(NULL
) + 1, &qabot_createfakeuser
, NULL
);
304 cp
= (channel
*)args
[1];
306 localjoinchannel(qabot_nick
, cp
);
307 localgetops(qabot_nick
, cp
);
313 void qabot_child_handler(nick
* me
, int type
, void** args
) {
316 qab_bot
* bot
= me
->exts
[qabot_nickext
];
328 sender
= (nick
*)args
[0];
329 cp
= (channel
*)args
[1];
330 text
= (char*)args
[2];
333 if (*(++text
) == '\0') {
334 sendnoticetouser(me
, sender
, "No command specified.");
337 cargc
= splitline((char*)text
, cargv
, 50, 0);
338 cmd
= findcommandintree(qabot_chancommands
, cargv
[0], 1);
340 sendnoticetouser(me
, sender
, "Unknown command.");
344 if ((cp
->index
== bot
->staff_chan
) && !(cmd
->level
& QAC_STAFFCHAN
)) {
345 sendnoticetouser(me
, sender
, "This command cannot be used in the staff channel.");
349 if ((cp
->index
== bot
->question_chan
) && !(cmd
->level
& QAC_QUESTIONCHAN
)) {
350 sendnoticetouser(me
, sender
, "This command cannot be used in the question channel.");
354 if (cmd
->maxparams
< (cargc
-1)) {
355 rejoinline(cargv
[cmd
->maxparams
], cargc
- (cmd
->maxparams
));
356 cargc
=(cmd
->maxparams
) + 1;
359 qabot_currentbot
= bot
;
360 qabot_currentchan
= cp
;
362 (cmd
->handler
)((void*)sender
, cargc
- 1, &(cargv
[1]));
364 else if ((*text
!= '!') && (bot
->micnumeric
== sender
->numeric
) && (cp
->index
== bot
->staff_chan
)) {
365 bot
->lastmic
= time(NULL
);
369 s
= (qab_spam
*)malloc(sizeof(qab_spam
));
370 s
->message
= strdup(text
);
372 bot
->lastspam
->next
= s
;
376 if ((bot
->spamtime
+ bot
->spam_interval
) < time(0)) {
377 sendmessagetochannel(me
, bot
->public_chan
->channel
, "%s", text
);
379 bot
->spamtime
= time(0);
384 s
= (qab_spam
*)malloc(sizeof(qab_spam
));
385 s
->message
= strdup(text
);
387 bot
->nextspam
= bot
->lastspam
= s
;
388 scheduleoneshot(bot
->spamtime
+ bot
->spam_interval
, qabot_spam
, (void*)bot
);
392 else if ((*text
!= '!') && (bot
->recnumeric
== sender
->numeric
) && (cp
->index
== bot
->staff_chan
)) {
394 fprintf(bot
->recfile
, "%s\n", text
);
401 sender
= (nick
*)args
[0];
402 text
= (char*)args
[1];
405 lastmsg
= (time_t)sender
->exts
[qabot_spam_nickext
];
407 if ((lastmsg
+ bot
->ask_wait
) > time(0)) {
408 sendnoticetouser(me
, sender
, "You have already sent a question recently, please wait at least %d seconds between asking questions.", bot
->ask_wait
);
412 char hostbuf
[NICKLEN
+ USERLEN
+ HOSTLEN
+ 3];
415 unsigned long crc
= 0;
418 sendnoticetouser(me
, sender
, "Your question has been relayed to the %s staff.", bot
->public_chan
->name
->content
);
420 if (!getnumerichandlefromchanhash(bot
->public_chan
->channel
->users
, sender
->numeric
))
427 if ((len
> 0) && (text
[len
- 1] == '>')) {
428 text
[len
- 1] = '\0';
432 if ((len
< 5) || !strchr(text
, ' ') || (*text
== 1))
435 if ((bot
->flags
& QAB_AUTHEDONLY
) && !IsAccount(sender
))
438 if (bot
->flags
& QAB_CONTROLCHAR
) {
441 for (ch
= text
; *ch
; ch
++)
442 if ((*ch
== 2) || (*ch
== 3) || (*ch
== 22) || (*ch
== 27) || (*ch
== 31))
445 else if (bot
->flags
& QAB_COLOUR
) {
448 for (ch
= text
; *ch
; ch
++)
453 if (bot
->flags
& QAB_FLOODDETECT
) {
455 if (crc
== qab_lastq_crc
) {
457 if (qab_lastq_count
>= 3) {
458 if ((qab_lastq_count
== 3) || ((qab_lastq_count
% 10) == 0))
459 sendmessagetochannel(me
, bot
->question_chan
->channel
, "WARNING: Possible question flood detected%s", (bot
->flags
& QAB_FLOODSTOP
) ? " - AUTO IGNORED." : ".");
460 if (bot
->flags
& QAB_FLOODSTOP
)
470 sprintf(hostbuf
,"%s!%s@%s", sender
->nick
, sender
->ident
, sender
->host
->name
->content
);
472 for (b
= bot
->blocks
; b
; b
= b
->next
) {
474 case QABBLOCK_ACCOUNT
:
475 if (IsAccount(sender
) && !ircd_strncmp(sender
->authname
, b
->blockstr
, ACCOUNTLEN
))
480 if (!match(b
->blockstr
, hostbuf
))
485 if (!match(b
->blockstr
, text
))
491 sender
->exts
[qabot_spam_nickext
] = (void*)time(0);
493 q
= (qab_question
*)malloc(sizeof(qab_question
));
494 q
->id
= ++bot
->lastquestionID
;
495 q
->question
= strdup(text
);
497 strncpy(q
->nick
, sender
->nick
, NICKLEN
);
498 q
->nick
[NICKLEN
] = '\0';
499 q
->numeric
= sender
->numeric
;
500 q
->crc
= (bot
->flags
& QAB_FLOODDETECT
) ? crc
: 0;
502 q
->next
= bot
->questions
[q
->id
% QUESTIONHASHSIZE
];
504 bot
->questions
[q
->id
% QUESTIONHASHSIZE
] = q
;
506 sendmessagetochannel(me
, bot
->question_chan
->channel
, "ID: %3d <%s> %s", q
->id
, visiblehostmask(sender
, hostbuf
), text
);
508 if ((bot
->flags
& QAB_LINEBREAK
) && ((bot
->lastquestionID
% 10) == 0))
509 sendmessagetochannel(me
, bot
->question_chan
->channel
, "-----------------------------------");
515 scheduleoneshot(time(NULL
) + 1, &qabot_createbotfakeuser
, (void*)bot
);
519 cp
= (channel
*)args
[1];
521 localjoinchannel(bot
->np
, cp
);
522 localgetops(bot
->np
, cp
);
531 char* qabot_getvictim(nick
* np
, char* target
) {
532 if (*target
!= '#') {
535 if (!(victim
= getnickbynick(target
))) {
536 sendnoticetouser(qabot_nick
, np
, "No such nickname.");
540 if (!IsAccount(victim
)) {
541 sendnoticetouser(qabot_nick
, np
, "%s is not authed.", victim
->nick
);
545 return victim
->authname
;
551 const char* qabot_uflagtostr(flag_t flags
) {
557 if (flags
& QAUFLAG_ADMIN
) { buf
[i
++] = 'a'; }
558 if (flags
& QAUFLAG_DEVELOPER
) { buf
[i
++] = 'd'; }
559 if (flags
& QAUFLAG_STAFF
) { buf
[i
++] = 's'; }
566 const char* qabot_formattime(time_t tme
) {
571 strftime(buf
, 15, "%d/%m/%y %H:%M", tmp
);
576 qab_bot
* qabot_getcurrentbot() {
577 return qabot_currentbot
;
580 channel
* qabot_getcurrentchannel() {
581 return qabot_currentchan
;