]>
jfr.im git - irc/quakenet/newserv.git/blob - qabot/qabot.c
a24a6f9c442a8cc212da03ac55006c46d1003885
7 #include "../nick/nick.h"
8 #include "../localuser/localuserchannel.h"
9 #include "../core/hooks.h"
10 #include "../core/schedule.h"
11 #include "../lib/array.h"
12 #include "../lib/base64.h"
13 #include "../lib/irc_string.h"
14 #include "../lib/splitline.h"
20 int qabot_spam_nickext
;
23 CommandTree
* qabot_commands
;
24 CommandTree
* qabot_chancommands
;
25 qab_bot
* qab_bots
= 0;
26 unsigned long qab_lastq_crc
= 0;
27 int qab_lastq_count
= 0;
28 qab_bot
* qabot_currentbot
= 0;
29 channel
* qabot_currentchan
= 0;
32 qab_startime
= time(0);
34 registerhook(HOOK_NICK_LOSTNICK
, qabot_lostnick
);
35 registerhook(HOOK_CHANNEL_PART
, qabot_channel_part
);
37 qabot_commands
= newcommandtree();
38 addcommandtotree(qabot_commands
, "showcommands", 0, 0, qabot_doshowcommands
);
39 addcommandtotree(qabot_commands
, "help", QAUFLAG_STAFF
, 1, qabot_dohelp
);
40 addcommandtotree(qabot_commands
, "hello", 0, 0, qabot_dohello
);
41 addcommandtotree(qabot_commands
, "save", QAUFLAG_ADMIN
, 0, qabot_dosave
);
42 addcommandtotree(qabot_commands
, "listbots", QAUFLAG_STAFF
, 0, qabot_dolistbots
);
43 addcommandtotree(qabot_commands
, "listusers", QAUFLAG_STAFF
, 0, qabot_dolistusers
);
44 addcommandtotree(qabot_commands
, "showbot", QAUFLAG_STAFF
, 1, qabot_doshowbot
);
45 addcommandtotree(qabot_commands
, "addbot", QAUFLAG_STAFF
, 6, qabot_doaddbot
);
46 addcommandtotree(qabot_commands
, "delbot", QAUFLAG_STAFF
, 1, qabot_dodelbot
);
47 addcommandtotree(qabot_commands
, "adduser", QAUFLAG_ADMIN
, 2, qabot_doadduser
);
48 addcommandtotree(qabot_commands
, "changelev", QAUFLAG_ADMIN
, 2, qabot_dochangelev
);
49 addcommandtotree(qabot_commands
, "deluser", QAUFLAG_ADMIN
, 1, qabot_dodeluser
);
50 addcommandtotree(qabot_commands
, "whois", QAUFLAG_STAFF
, 1, qabot_dowhois
);
51 addcommandtotree(qabot_commands
, "status", QAUFLAG_DEVELOPER
, 0, qabot_dostatus
);
53 qabot_chancommands
= newcommandtree();
54 addcommandtotree(qabot_chancommands
, "answer", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 2, qabot_dochananswer
);
55 addcommandtotree(qabot_chancommands
, "block", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 2, qabot_dochanblock
);
56 addcommandtotree(qabot_chancommands
, "clear", QAC_STAFFCHAN
, 0, qabot_dochanclear
);
57 addcommandtotree(qabot_chancommands
, "closechan", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 0, qabot_dochanclosechan
);
58 addcommandtotree(qabot_chancommands
, "config", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 2, qabot_dochanconfig
);
59 addcommandtotree(qabot_chancommands
, "help", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 1, qabot_dochanhelp
);
60 addcommandtotree(qabot_chancommands
, "listblocks", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 0, qabot_dochanlistblocks
);
61 addcommandtotree(qabot_chancommands
, "mic", QAC_STAFFCHAN
, 0, qabot_dochanmic
);
62 addcommandtotree(qabot_chancommands
, "moo", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 0, qabot_dochanmoo
);
63 addcommandtotree(qabot_chancommands
, "offtopic", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 30, qabot_dochanofftopic
);
64 addcommandtotree(qabot_chancommands
, "openchan", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 0, qabot_dochanopenchan
);
65 addcommandtotree(qabot_chancommands
, "ping", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 0, qabot_dochanping
);
66 addcommandtotree(qabot_chancommands
, "reset", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 1, qabot_dochanreset
);
67 addcommandtotree(qabot_chancommands
, "spam", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 30, qabot_dochanspam
);
68 addcommandtotree(qabot_chancommands
, "status", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 0, qabot_dochanstatus
);
69 addcommandtotree(qabot_chancommands
, "unblock", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 2, qabot_dochanunblock
);
70 addcommandtotree(qabot_chancommands
, "listtexts", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 0, qabot_dochanlisttexts
);
71 addcommandtotree(qabot_chancommands
, "showsection", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 2, qabot_dochanshowsection
);
72 addcommandtotree(qabot_chancommands
, "addtext", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 2, qabot_dochanaddtext
);
73 addcommandtotree(qabot_chancommands
, "deltext", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 1, qabot_dochandeltext
);
74 addcommandtotree(qabot_chancommands
, "addsection", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 2, qabot_dochanaddsection
);
75 addcommandtotree(qabot_chancommands
, "delsection", QAC_QUESTIONCHAN
|QAC_STAFFCHAN
, 2, qabot_dochandelsection
);
76 addcommandtotree(qabot_chancommands
, "record", QAC_STAFFCHAN
, 2, qabot_dochanrecord
);
78 if ((qabot_nickext
= registernickext("QABOT")) == -1) {
82 if ((qabot_spam_nickext
= registernickext("QABOT_SPAM")) == -1) {
88 scheduleoneshot(time(NULL
) + 1, &qabot_createfakeuser
, NULL
);
89 scheduleoneshot(time(NULL
) + QABOT_SAVEWAIT
, &qabot_savetimer
, NULL
);
93 deregisterhook(HOOK_NICK_LOSTNICK
, qabot_lostnick
);
94 deregisterhook(HOOK_CHANNEL_PART
, qabot_channel_part
);
96 deleteschedule(0, qabot_savetimer
, NULL
);
101 qabot_delbot(qab_bots
);
103 if (qabot_nickext
!= -1)
104 releasenickext(qabot_nickext
);
106 if (qabot_spam_nickext
!= -1)
107 releasenickext(qabot_spam_nickext
);
110 deregisterlocaluser(qabot_nick
, "Module unloaded.");
115 qabot_squelchuser(qabot_users
);
117 deletecommandfromtree(qabot_commands
, "showcommands", qabot_doshowcommands
);
118 deletecommandfromtree(qabot_commands
, "help", qabot_dohelp
);
119 deletecommandfromtree(qabot_commands
, "hello", qabot_dohello
);
120 deletecommandfromtree(qabot_commands
, "save", qabot_dosave
);
121 deletecommandfromtree(qabot_commands
, "listbots", qabot_dolistbots
);
122 deletecommandfromtree(qabot_commands
, "listusers", qabot_dolistusers
);
123 deletecommandfromtree(qabot_commands
, "showbot", qabot_doshowbot
);
124 deletecommandfromtree(qabot_commands
, "addbot", qabot_doaddbot
);
125 deletecommandfromtree(qabot_commands
, "delbot", qabot_dodelbot
);
126 deletecommandfromtree(qabot_commands
, "adduser", qabot_doadduser
);
127 deletecommandfromtree(qabot_commands
, "changelev", qabot_dochangelev
);
128 deletecommandfromtree(qabot_commands
, "deluser", qabot_dodeluser
);
129 deletecommandfromtree(qabot_commands
, "whois", qabot_dowhois
);
130 deletecommandfromtree(qabot_commands
, "status", qabot_dostatus
);
131 destroycommandtree(qabot_commands
);
133 deletecommandfromtree(qabot_chancommands
, "answer", qabot_dochananswer
);
134 deletecommandfromtree(qabot_chancommands
, "block", qabot_dochanblock
);
135 deletecommandfromtree(qabot_chancommands
, "clear", qabot_dochanclear
);
136 deletecommandfromtree(qabot_chancommands
, "closechan", qabot_dochanclosechan
);
137 deletecommandfromtree(qabot_chancommands
, "config", qabot_dochanconfig
);
138 deletecommandfromtree(qabot_chancommands
, "help", qabot_dochanhelp
);
139 deletecommandfromtree(qabot_chancommands
, "listblocks", qabot_dochanlistblocks
);
140 deletecommandfromtree(qabot_chancommands
, "mic", qabot_dochanmic
);
141 deletecommandfromtree(qabot_chancommands
, "moo", qabot_dochanmoo
);
142 deletecommandfromtree(qabot_chancommands
, "offtopic", qabot_dochanofftopic
);
143 deletecommandfromtree(qabot_chancommands
, "openchan", qabot_dochanopenchan
);
144 deletecommandfromtree(qabot_chancommands
, "ping", qabot_dochanping
);
145 deletecommandfromtree(qabot_chancommands
, "reset", qabot_dochanreset
);
146 deletecommandfromtree(qabot_chancommands
, "spam", qabot_dochanspam
);
147 deletecommandfromtree(qabot_chancommands
, "status", qabot_dochanstatus
);
148 deletecommandfromtree(qabot_chancommands
, "unblock", qabot_dochanunblock
);
149 deletecommandfromtree(qabot_chancommands
, "listtexts", qabot_dochanlisttexts
);
150 deletecommandfromtree(qabot_chancommands
, "showsection", qabot_dochanshowsection
);
151 deletecommandfromtree(qabot_chancommands
, "addtext", qabot_dochanaddtext
);
152 deletecommandfromtree(qabot_chancommands
, "deltext", qabot_dochandeltext
);
153 deletecommandfromtree(qabot_chancommands
, "addsection", qabot_dochanaddsection
);
154 deletecommandfromtree(qabot_chancommands
, "delsection", qabot_dochandelsection
);
155 deletecommandfromtree(qabot_chancommands
, "record", qabot_dochanrecord
);
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
, "%s deactivated.",
173 bot
->recording_section
? "Recorder" : "Mic");
174 if (!b
->lastspam
&& !b
->recording_section
)
175 qabot_spamstored((void*)b
);
176 b
->recording_section
= 0;
181 void qabot_channel_part(int hooknum
, void* arg
) {
182 channel
* cp
= (channel
*)((void**)arg
)[0];
183 nick
* np
= (nick
*)((void**)arg
)[1];
192 for (b
= qab_bots
; b
; b
= b
->next
) {
193 if ((b
->micnumeric
== np
->numeric
) && (b
->staff_chan
->channel
== cp
)) {
195 sendmessagetochannel(b
->np
, b
->staff_chan
->channel
, "%s deactivated.",
196 b
->recording_section
? "Recorder" : "Mic");
197 if (!b
->lastspam
&& !b
->recording_section
)
198 qabot_spamstored((void*)b
);
199 b
->recording_section
= 0;
204 void qabot_createfakeuser(void* arg
) {
207 if ((qabot_nick
= registerlocaluser(QABOT_NICK
, QABOT_USER
, QABOT_HOST
,
208 QABOT_NAME
, QABOT_ACCT
, QABOT_UMDE
, &qabot_handler
))) {
209 if ((cp
= findchannel(QABOT_HOMECHAN
))) {
210 localjoinchannel(qabot_nick
, cp
);
211 localgetops(qabot_nick
, cp
);
214 localcreatechannel(qabot_nick
, QABOT_HOMECHAN
);
218 void qabot_handler(nick
* me
, int type
, void** args
) {
229 sender
= (nick
*)args
[0];
231 if (!strncmp("\001VERSION", args
[1], 8)) {
232 sendnoticetouser(qabot_nick
, sender
, "\001VERSION %s\001", QABOT_NAME
);
236 cargc
= splitline((char*)args
[1], cargv
, 50, 0);
237 cmd
= findcommandintree(qabot_commands
, cargv
[0], 1);
239 sendnoticetouser(qabot_nick
, sender
, "Unknown command.");
243 if (!IsAccount(sender
)) {
244 sendnoticetouser(qabot_nick
, sender
, "You must be authed to use this command.");
248 u
= qabot_getuser(sender
->authname
);
251 sendnoticetouser(qabot_nick
, sender
, "You need an account to use this command.");
255 if ((cmd
->level
& QAUFLAG_STAFF
) && !QAIsStaff(u
)) {
256 sendnoticetouser(qabot_nick
, sender
, "You do not have access to this command.");
260 if ((cmd
->level
& QAUFLAG_ADMIN
) && !QAIsAdmin(u
)) {
261 sendnoticetouser(qabot_nick
, sender
, "You do not have access to this command.");
265 if ((cmd
->level
& QAUFLAG_DEVELOPER
) && !QAIsDeveloper(u
)) {
266 sendnoticetouser(qabot_nick
, sender
, "You do not have access to this command.");
271 if (cmd
->maxparams
< (cargc
-1)) {
272 rejoinline(cargv
[cmd
->maxparams
], cargc
- (cmd
->maxparams
));
273 cargc
=(cmd
->maxparams
) + 1;
276 (cmd
->handler
)((void*)sender
, cargc
- 1, &(cargv
[1]));
282 scheduleoneshot(time(NULL
) + 1, &qabot_createfakeuser
, NULL
);
286 cp
= (channel
*)args
[1];
288 localjoinchannel(qabot_nick
, cp
);
289 localgetops(qabot_nick
, cp
);
295 void qabot_child_handler(nick
* me
, int type
, void** args
) {
298 qab_bot
* bot
= me
->exts
[qabot_nickext
];
310 sender
= (nick
*)args
[0];
311 cp
= (channel
*)args
[1];
312 text
= (char*)args
[2];
315 cargc
= splitline((char*)(++text
), cargv
, 50, 0);
316 cmd
= findcommandintree(qabot_chancommands
, cargv
[0], 1);
318 sendnoticetouser(me
, sender
, "Unknown command.");
322 if ((cp
->index
== bot
->staff_chan
) && !(cmd
->level
& QAC_STAFFCHAN
)) {
323 sendnoticetouser(me
, sender
, "This command cannot be used in the staff channel.");
327 if ((cp
->index
== bot
->question_chan
) && !(cmd
->level
& QAC_QUESTIONCHAN
)) {
328 sendnoticetouser(me
, sender
, "This command cannot be used in the question channel.");
332 if (cmd
->maxparams
< (cargc
-1)) {
333 rejoinline(cargv
[cmd
->maxparams
], cargc
- (cmd
->maxparams
));
334 cargc
=(cmd
->maxparams
) + 1;
337 qabot_currentbot
= bot
;
338 qabot_currentchan
= cp
;
340 (cmd
->handler
)((void*)sender
, cargc
- 1, &(cargv
[1]));
342 else if ((*text
!= '!') && (bot
->micnumeric
== sender
->numeric
) && (cp
->index
== bot
->staff_chan
)) {
343 bot
->lastmic
= time(NULL
);
347 s
= (qab_spam
*)malloc(sizeof(qab_spam
));
348 s
->message
= strdup(text
);
350 bot
->lastspam
->next
= s
;
354 if ((bot
->spamtime
+ bot
->spam_interval
) < time(0)) {
355 sendmessagetochannel(me
, bot
->public_chan
->channel
, "%s", text
);
357 bot
->spamtime
= time(0);
362 s
= (qab_spam
*)malloc(sizeof(qab_spam
));
363 s
->message
= strdup(text
);
365 bot
->nextspam
= bot
->lastspam
= s
;
366 scheduleoneshot(bot
->spamtime
+ bot
->spam_interval
, qabot_spam
, (void*)bot
);
374 sender
= (nick
*)args
[0];
375 text
= (char*)args
[1];
378 lastmsg
= (time_t)sender
->exts
[qabot_spam_nickext
];
380 if ((lastmsg
+ bot
->ask_wait
) > time(0)) {
381 sendnoticetouser(me
, sender
, "You have already sent a question recently, please wait at least %d seconds between asking questions.", bot
->ask_wait
);
385 char hostbuf
[NICKLEN
+ USERLEN
+ HOSTLEN
+ 3];
391 sendnoticetouser(me
, sender
, "Your question has been relayed to the %s staff.", bot
->public_chan
->name
->content
);
393 if (!getnumerichandlefromchanhash(bot
->public_chan
->channel
->users
, sender
->numeric
))
400 if ((len
> 0) && (text
[len
- 1] == '>')) {
401 text
[len
- 1] = '\0';
405 if ((len
< 5) || !strchr(text
, ' ') || (*text
== 1))
408 if ((bot
->flags
& QAB_AUTHEDONLY
) && !IsAccount(sender
))
411 if (bot
->flags
& QAB_CONTROLCHAR
) {
414 for (ch
= text
; *ch
; ch
++)
415 if ((*ch
== 2) || (*ch
== 3) || (*ch
== 22) || (*ch
== 27) || (*ch
== 31))
418 else if (bot
->flags
& QAB_COLOUR
) {
421 for (ch
= text
; *ch
; ch
++)
426 if (bot
->flags
& QAB_FLOODDETECT
) {
428 if (crc
== qab_lastq_crc
) {
430 if (qab_lastq_count
>= 3) {
431 if ((qab_lastq_count
== 3) || ((qab_lastq_count
% 10) == 0))
432 sendmessagetochannel(me
, bot
->question_chan
->channel
, "WARNING: Possible question flood detected%s", (bot
->flags
& QAB_FLOODSTOP
) ? " - AUTO IGNORED." : ".");
433 if (bot
->flags
& QAB_FLOODSTOP
)
443 sprintf(hostbuf
,"%s!%s@%s", sender
->nick
, sender
->ident
, sender
->host
->name
->content
);
445 for (b
= bot
->blocks
; b
; b
= b
->next
) {
447 case QABBLOCK_ACCOUNT
:
448 if (IsAccount(sender
) && !ircd_strncmp(sender
->authname
, b
->blockstr
, ACCOUNTLEN
))
453 if (!match(b
->blockstr
, hostbuf
))
458 if (!match(b
->blockstr
, text
))
464 sender
->exts
[qabot_spam_nickext
] = (void*)time(0);
466 q
= (qab_question
*)malloc(sizeof(qab_question
));
467 q
->id
= ++bot
->lastquestionID
;
468 q
->question
= strdup(text
);
470 strncpy(q
->nick
, sender
->nick
, NICKLEN
);
471 q
->nick
[NICKLEN
] = '\0';
472 q
->numeric
= sender
->numeric
;
473 q
->crc
= (bot
->flags
& QAB_FLOODDETECT
) ? crc
: 0;
475 q
->next
= bot
->questions
[q
->id
% QUESTIONHASHSIZE
];
477 bot
->questions
[q
->id
% QUESTIONHASHSIZE
] = q
;
479 sendmessagetochannel(me
, bot
->question_chan
->channel
, "ID: %3d <%s> %s", q
->id
, visiblehostmask(sender
, hostbuf
), text
);
481 if ((bot
->flags
& QAB_LINEBREAK
) && ((bot
->lastquestionID
% 10) == 0))
482 sendmessagetochannel(me
, bot
->question_chan
->channel
, "-----------------------------------");
488 scheduleoneshot(time(NULL
) + 1, &qabot_createbotfakeuser
, (void*)bot
);
492 cp
= (channel
*)args
[1];
494 localjoinchannel(bot
->np
, cp
);
495 localgetops(bot
->np
, cp
);
504 char* qabot_getvictim(nick
* np
, char* target
) {
505 if (*target
!= '#') {
508 if (!(victim
= getnickbynick(target
))) {
509 sendnoticetouser(qabot_nick
, np
, "No such nickname.");
513 if (!IsAccount(victim
)) {
514 sendnoticetouser(qabot_nick
, np
, "%s is not authed.", victim
->nick
);
518 return victim
->authname
;
524 const char* qabot_uflagtostr(flag_t flags
) {
530 if (flags
& QAUFLAG_ADMIN
) { buf
[i
++] = 'a'; }
531 if (flags
& QAUFLAG_DEVELOPER
) { buf
[i
++] = 'd'; }
532 if (flags
& QAUFLAG_STAFF
) { buf
[i
++] = 's'; }
539 const char* qabot_formattime(time_t tme
) {
544 strftime(buf
, 15, "%d/%m/%y %H:%M", tmp
);
549 qab_bot
* qabot_getcurrentbot() {
550 return qabot_currentbot
;
553 channel
* qabot_getcurrentchannel() {
554 return qabot_currentchan
;