]> jfr.im git - irc/quakenet/newserv.git/blob - tutorbot/tutorbot.c
Merge.
[irc/quakenet/newserv.git] / tutorbot / tutorbot.c
1 #include <stdio.h>
2 #include <string.h>
3
4 #include "../nick/nick.h"
5 #include "../localuser/localuserchannel.h"
6 #include "../core/schedule.h"
7 #include "../lib/irc_string.h"
8 #include "../lib/version.h"
9
10 MODULE_VERSION("");
11
12 #include <stdio.h>
13
14 #define STAFFCHAN "#tutorial.staff"
15 #define QUESTIONCHAN "#tutorial.questions"
16 #define PUBLICCHAN "#tutorial"
17
18 #define QUESTIONINTERVAL 30
19
20 #define SPAMINTERVAL 10
21
22 #define NICKMONITORTIME 300
23
24 #define QUESTIONHASHSIZE 1000
25
26 #define QUESTION_NEW 0x0
27 #define QUESTION_ANSWERED 0x1
28 #define QUESTION_OFFTOPIC 0x2
29 #define QUESTION_SPAM 0x3
30
31 #define QUESTION_STATE 0x7
32
33 typedef struct spammsg {
34 sstring *message;
35 struct spammsg *next;
36 } spammsg;
37
38 typedef struct questrec {
39 int ID;
40 sstring *question;
41 flag_t flags;
42 char nick[NICKLEN+1];
43 unsigned long numeric;
44 sstring *answer;
45 struct questrec *next;
46 } questrec;
47
48 typedef struct blockeduser {
49 int type; /* 0 = account, 1 = hostmask */
50 char content[NICKLEN+USERLEN+HOSTLEN+3]; /* now includes a mask or an accountname */
51 struct blockeduser *next;
52 } blockeduser;
53
54 typedef struct storedanswer {
55 questrec *qptr;
56 char nick[NICKLEN+1];
57 struct storedanswer *next;
58 } storedanswer;
59
60
61 nick *tutornick;
62 int tutornext;
63 unsigned int micnumeric; /* Who has the mic atm */
64 spammsg *nextspam;
65 spammsg *lastspam;
66 time_t spamtime;
67 int lastquestionID;
68 questrec *questiontable[QUESTIONHASHSIZE];
69 blockeduser *blockedusers;
70 storedanswer *storedanswers;
71
72 void spamstored();
73 void tutorhandler(nick *me, int type, void **args);
74
75 void tutorspam(void *arg) {
76 channel *cp;
77 spammsg *smp;
78
79 if (tutornick && (cp=findchannel(PUBLICCHAN))) {
80 sendmessagetochannel(tutornick, cp, "%s", nextspam->message->content);
81 }
82
83 smp=nextspam;
84 nextspam=nextspam->next;
85
86 freesstring(smp->message);
87 free(smp);
88
89 spamtime=time(NULL);
90
91 if (nextspam) {
92 scheduleoneshot(spamtime+SPAMINTERVAL, tutorspam, NULL);
93 } else {
94 lastspam=NULL;
95 if (!micnumeric)
96 spamstored();
97 }
98 }
99
100 void spamstored(void) {
101 storedanswer *nsa;
102 channel *cp;
103
104 cp=findchannel(PUBLICCHAN);
105
106 if (!cp || !tutornick)
107 return;
108
109 while(storedanswers) {
110 nsa=storedanswers;
111 sendmessagetochannel(tutornick,cp,"%s asked: %s",storedanswers->qptr->nick,storedanswers->qptr->question->content);
112 sendmessagetochannel(tutornick,cp,"%s answered: %s",storedanswers->nick,storedanswers->qptr->answer->content);
113
114 storedanswers=storedanswers->next;
115 free(nsa);
116 }
117 }
118
119 void usercountspam(void *arg) {
120 channel *cp, *cp2;
121
122 if (!(cp=findchannel(PUBLICCHAN)))
123 return;
124
125 if (!(cp2=findchannel(STAFFCHAN)))
126 return;
127
128 if (!tutornick)
129 return;
130
131 sendmessagetochannel(tutornick, cp2, "Currently %d users in %s.",cp->users->totalusers,cp->index->name->content);
132 }
133
134 void flushspamqueue() {
135 spammsg *smp,*nsmp;
136
137 for (smp=nextspam;smp;smp=nsmp) {
138 nsmp=smp->next;
139 freesstring(smp->message);
140 free(smp);
141 }
142
143 deleteallschedules(tutorspam);
144 nextspam=lastspam=NULL;
145 }
146
147 void _init() {
148 channel *cp;
149
150 if ((tutornext=registernickext("tutorbot")) > -1) {
151 micnumeric=0;
152 nextspam=lastspam=NULL;
153 blockedusers=NULL;
154 storedanswers=NULL;
155 spamtime=0;
156 lastquestionID=0;
157 memset(questiontable, 0, sizeof(questrec *) * QUESTIONHASHSIZE);
158 tutornick=registerlocaluser("Tutor","tutor","quakenet.org","#tutorial bot","tutor",UMODE_ACCOUNT | UMODE_INV,
159 tutorhandler);
160
161 if ((cp=findchannel(PUBLICCHAN))) {
162 localjoinchannel(tutornick,cp);
163 localgetops(tutornick, cp);
164 } else {
165 localcreatechannel(tutornick,PUBLICCHAN);
166 }
167
168 if ((cp=findchannel(QUESTIONCHAN))) {
169 localjoinchannel(tutornick,cp);
170 } else {
171 localcreatechannel(tutornick,QUESTIONCHAN);
172 }
173
174 if ((cp=findchannel(STAFFCHAN))) {
175 localjoinchannel(tutornick,cp);
176 } else {
177 localcreatechannel(tutornick,STAFFCHAN);
178 }
179
180 schedulerecurring(time(NULL)+300, 0, 300, usercountspam, NULL);
181 } else {
182 Error("tutorbot",ERR_ERROR,"Unable to get nickext");
183 }
184 }
185
186 void _fini() {
187 int i;
188 questrec *qrp, *nqrp;
189 blockeduser *bup;
190
191 if (tutornext > -1) {
192 releasenickext(tutornext);
193 deregisterlocaluser(tutornick,NULL);
194 deleteallschedules(usercountspam);
195 flushspamqueue();
196
197 /* remove all blockedusers */
198 while (blockedusers!=NULL) {
199 bup=blockedusers;
200 blockedusers=blockedusers->next;
201 free(bup);
202 }
203
204 /* Blow away all stored questions */
205 for(i=0;i<QUESTIONHASHSIZE;i++) {
206 for (qrp=questiontable[i];qrp;qrp=nqrp) {
207 nqrp=qrp->next;
208 freesstring(qrp->question);
209 freesstring(qrp->answer);
210 free(qrp);
211 }
212 }
213 }
214 }
215
216 void tutordump() {
217 FILE *fp;
218 int i;
219 questrec *qrp;
220 int j;
221
222 if (!(fp=fopen("tutor-questions","w")))
223 return;
224
225 for (i=0;i<4;i++) {
226 switch(i) {
227 case QUESTION_NEW:
228 fprintf(fp,"Untouched questions:\n\n");
229 break;
230
231 case QUESTION_ANSWERED:
232 fprintf(fp,"\nAnswered questions:\n\n");
233 break;
234
235 case QUESTION_OFFTOPIC:
236 fprintf(fp,"\nOff-topic questions:\n\n");
237 break;
238
239 case QUESTION_SPAM:
240 fprintf(fp,"\nSpam:\n\n");
241 break;
242 }
243
244 for (j=0;j<=lastquestionID;j++) {
245 for (qrp=questiontable[j%QUESTIONHASHSIZE];qrp;qrp=qrp->next)
246 if (qrp->ID==j)
247 break;
248
249 if (!qrp)
250 continue;
251
252 if ((qrp->flags & QUESTION_STATE) == i) {
253 fprintf(fp,"(%s) %s\n",qrp->nick,qrp->question->content);
254
255 if (i==QUESTION_ANSWERED)
256 fprintf(fp,"Answer: %s\n\n",qrp->answer->content);
257 }
258 }
259 }
260
261 fclose(fp);
262 }
263
264 void tutorhandler(nick *me, int type, void **args) {
265 nick *np;
266 channel *cp;
267 char *text;
268 spammsg *smp;
269 time_t lastmsg;
270
271 if (type==LU_CHANMSG) {
272 np=args[0];
273 cp=args[1];
274 text=args[2];
275
276 /* Staff channel *only* commands */
277 if (!ircd_strcmp(cp->index->name->content,STAFFCHAN)) {
278 if (!ircd_strncmp(text,"!mic",4)) {
279 if (micnumeric) {
280 if (micnumeric==np->numeric) {
281 micnumeric=0;
282 sendmessagetochannel(me,cp,"Mic deactivated.");
283 if (!lastspam)
284 spamstored();
285
286 } else {
287 micnumeric=np->numeric;
288 sendmessagetochannel(me,cp,"%s now has the mic. Anything said by %s will be relayed in %s.",
289 np->nick, np->nick, PUBLICCHAN);
290 }
291 } else {
292 micnumeric=np->numeric;
293 sendmessagetochannel(me,cp,"Mic activated. Anything said by %s will be relayed in %s.",
294 np->nick, PUBLICCHAN);
295 }
296 } else if (!ircd_strncmp(text,"!m00",4)) {
297 /* mandatory m00 command */
298 int i,maxm00=rand()%100;
299 char m00[101];
300
301 for(i=0;i<maxm00;i++) {
302 int mt=rand()%3;
303 if (mt==0) {
304 m00[i]='o';
305 } else if (mt==1) {
306 m00[i]='0';
307 } else {
308 m00[i]='O';
309 }
310 }
311 m00[i]='\0';
312
313 sendmessagetochannel(me,cp,"m%s",m00);
314 } else if (!ircd_strncmp(text,"!clear",6)) {
315 flushspamqueue();
316 sendmessagetochannel(me,cp,"Cleared message buffer.");
317 if (micnumeric) {
318 micnumeric=0;
319 sendmessagetochannel(me,cp,"Mic deactivated.");
320 }
321 } else if (!ircd_strncmp(text,"!save",5)) {
322 tutordump();
323 sendnoticetouser(me,np,"Save done.");
324 } else if (*text != '!' && np->numeric==micnumeric) {
325 /* Message to be relayed */
326 if (lastspam) {
327 smp=(spammsg *)malloc(sizeof(spammsg));
328 smp->message=getsstring(text, 500);
329 smp->next=NULL;
330 lastspam->next=smp;
331 lastspam=smp;
332 } else {
333 if (spamtime + SPAMINTERVAL < time(NULL)) {
334 /* Spam it directly */
335 if ((cp=findchannel(PUBLICCHAN))) {
336 sendmessagetochannel(me, cp, "%s", text);
337 }
338 spamtime=time(NULL);
339 } else {
340 /* Queue it and start the clock */
341 smp=(spammsg *)malloc(sizeof(spammsg));
342 smp->message=getsstring(text, 500);
343 smp->next=NULL;
344 lastspam=nextspam=smp;
345 scheduleoneshot(spamtime+SPAMINTERVAL, tutorspam, NULL);
346 }
347 }
348 }
349 }
350
351 /* Commands for staff channel or question channel */
352 if (!ircd_strcmp(cp->index->name->content,STAFFCHAN) || !ircd_strcmp(cp->index->name->content,QUESTIONCHAN)) {
353 if (!ircd_strncmp(text, "!spam ", 6)) {
354 int qid;
355 char *ch=text+5;
356 questrec *qrp;
357
358 while (*ch) {
359 while (*(++ch) == ' '); /* skip spaces */
360
361 qid=strtol(ch, &ch, 10);
362
363 if (qid < 1 || qid > lastquestionID) {
364 sendnoticetouser(me,np,"Invalid question ID %d.",qid);
365 continue;
366 }
367
368 for (qrp=questiontable[qid%QUESTIONHASHSIZE];qrp;qrp=qrp->next)
369 if (qrp->ID==qid)
370 break;
371
372 if (!qrp) {
373 sendnoticetouser(me,np,"Can't find question %d.",qid);
374 continue;
375 }
376
377 qrp->flags = ((qrp->flags) & ~QUESTION_STATE) | QUESTION_SPAM;
378 sendnoticetouser(me,np,"Qustion %d has been marked as spam.",qid);
379 }
380 } else if (!ircd_strncmp(text, "!offtopic ", 10)) {
381 int qid;
382 char *ch=text+9;
383 questrec *qrp;
384
385 while (*ch) {
386 while (*(++ch) == ' '); /* skip spaces */
387
388 qid=strtol(ch, &ch, 10);
389
390 if (qid < 1 || qid > lastquestionID) {
391 sendnoticetouser(me,np,"Invalid question ID %d.",qid);
392 continue;
393 }
394
395 for (qrp=questiontable[qid%QUESTIONHASHSIZE];qrp;qrp=qrp->next)
396 if (qrp->ID==qid)
397 break;
398
399 if (!qrp) {
400 sendnoticetouser(me,np,"Can't find question %d.",qid);
401 continue;
402 }
403
404 qrp->flags = ((qrp->flags) & ~QUESTION_STATE) | QUESTION_OFFTOPIC;
405 sendnoticetouser(me,np,"Qustion %d has been marked as off-topic.",qid);
406 }
407 } else if (!ircd_strncmp(text, "!reset ",7)) {
408 if (!ircd_strncmp(text+7,"blocks",6) || !ircd_strncmp(text+7,"all",3)) {
409 /* reset blocked users */
410 blockeduser *bup;
411
412 while(blockedusers) {
413 bup=blockedusers;
414 blockedusers=blockedusers->next;
415 free(bup);
416 }
417
418 sendnoticetouser(me,np,"Reset (blocks): Done.");
419
420 /* XXX: Grimless alert: only finish here if user asked for "blocks";
421 * "all" case must drop down to below" */
422 if (!ircd_strncmp(text+7,"blocks",6))
423 return;
424 }
425
426 if (!ircd_strncmp(text+7,"questions",9) || !ircd_strncmp(text+7,"all",3)) {
427 /* reset questions */
428 int i;
429 questrec *qrp,*nqrp;
430
431 for(i=0;i<QUESTIONHASHSIZE;i++) {
432 for(qrp=questiontable[i];qrp;qrp=nqrp) {
433 nqrp=qrp->next;
434
435 freesstring(qrp->question);
436 freesstring(qrp->answer);
437 free(qrp);
438 }
439 questiontable[i]=NULL;
440 }
441
442 lastquestionID=0;
443 sendnoticetouser(me,np,"Reset (questions): Done.");
444
445 return;
446 }
447
448 sendnoticetouser(me,np,"Unknown parameter: %s",text+7);
449 return;
450 } else if (!ircd_strncmp(text, "!reset",6)) {
451 sendnoticetouser(me,np,"!reset: more parameters needed.");
452 sendnoticetouser(me,np," <all|questions|blocks>");
453 return;
454 } else if (!ircd_strncmp(text, "!listblocks", 11)) {
455 blockeduser *bu;
456
457 if (!blockedusers) {
458 sendnoticetouser(me,np,"There are no blocked users.");
459 return;
460 }
461
462 sendnoticetouser(me,np,"Type Hostmask/Account");
463
464 for(bu=blockedusers;bu;bu=bu->next) {
465 if (!bu->type) {
466 sendnoticetouser(me,np,"A %s",bu->content);
467 } else {
468 sendnoticetouser(me,np,"H %s",bu->content);
469 }
470 }
471
472 sendnoticetouser(me,np,"End of list.");
473 return;
474 } else if (!ircd_strncmp(text, "!closechan", 10)) {
475 channel *cp;
476 modechanges changes;
477 if (!(cp=findchannel(PUBLICCHAN))) {
478 sendnoticetouser(me,np,"Can't find public channel!");
479 return;
480 }
481 localsetmodeinit(&changes,cp,me);
482 localdosetmode_simple(&changes,CHANMODE_INVITEONLY,0);
483 localsetmodeflush(&changes,1);
484 sendnoticetouser(me,np,"Public channel has been closed.");
485 return;
486 } else if (!ircd_strncmp(text, "!openchan", 9)) {
487 channel *cp;
488 modechanges changes;
489 if (!(cp=findchannel(PUBLICCHAN))) {
490 sendnoticetouser(me,np,"Can't find public channel!");
491 return;
492 }
493 localsetmodeinit(&changes,cp,me);
494 localdosetmode_simple(&changes,CHANMODE_MODERATE|CHANMODE_DELJOINS,CHANMODE_INVITEONLY);
495 localsetmodeflush(&changes,1);
496 sendnoticetouser(me,np,"Public channel has been opened.");
497 return;
498 } else if (!ircd_strncmp(text, "!unblock ", 9)) {
499 /* remove a block */
500 if (!ircd_strncmp(text+9,"-q",2)) {
501 /* remove a blocked accountname */
502 blockeduser *bu,*bu2=NULL;
503
504 if (text[12]=='#') {
505 for(bu=blockedusers;bu;bu=bu->next) {
506 if (!bu->type && !ircd_strncmp(text+13,bu->content,ACCOUNTLEN)) {
507 if (!bu2)
508 blockedusers = bu->next;
509 else
510 bu2->next = bu->next;
511
512 free(bu);
513 sendnoticetouser(me,np,"Block for users with accountname %s has been removed.",text+13);
514 return;
515 }
516 bu2 = bu;
517 }
518 } else {
519 nick *bun;
520 bun=getnickbynick(text+12);
521
522 if (!bun) {
523 sendnoticetouser(me,np,"Unkown user: %s.",text+12);
524 return;
525 }
526
527 if (!IsAccount(bun)) {
528 sendnoticetouser(me,np,"%s is not authed.",text+12);
529 return;
530 }
531
532 for(bu=blockedusers;bu;bu=bu->next) {
533 if (!bu->type && !ircd_strncmp(text+12,bu->content,ACCOUNTLEN)) {
534 if (!bu2)
535 blockedusers=bu->next;
536 else
537 bu2->next=bu->next;
538
539 free(bu);
540 sendnoticetouser(me,np,"Block for users with accountname %s has been removed.",text+12);
541 return;
542 }
543 bu2=bu;
544 }
545 }
546
547 sendnoticetouser(me,np,"No such blocked account %s.",text+12);
548 return;
549 } else {
550 /* it's a hostmask, check if the hostmask is valid */
551 char *textc=text+9;
552 blockeduser *bu,*bu2=NULL;
553
554 if (!strchr(textc,'@') || !strchr(textc,'!')) {
555 /* not a valid hostmask */
556 sendnoticetouser(me,np,"%s is not a valid hostmask.",textc);
557 return;
558 }
559
560 for(bu=blockedusers;bu;bu=bu->next) {
561 if (bu->type && !ircd_strncmp(textc,bu->content,ACCOUNTLEN+USERLEN+NICKLEN+2)) {
562 if (!bu2)
563 blockedusers = bu->next;
564 else
565 bu2->next = bu->next;
566
567 free(bu);
568 sendnoticetouser(me,np,"Block for users with a hostmask matching %s has been removed.",textc);
569 return;
570 }
571 bu2 = bu;
572 }
573
574 sendnoticetouser(me,np,"No such blocked hostmask %s.",textc);
575 return;
576 }
577 } else if (!ircd_strncmp(text, "!block ", 7)) {
578 /* Block a user */
579 char *nickp;
580
581 if (!ircd_strncmp(text+7,"-q",2)) {
582 /* block the user by his accountname */
583 blockeduser *bu;
584 nick *bun;
585 char *bptr;
586
587 nickp=text+10;
588 if (*nickp=='#') {
589 /* Account given so we will use it */
590 bptr=nickp+1;
591 } else {
592 bun=getnickbynick(nickp);
593
594 if (!bun) {
595 sendnoticetouser(me,np,"Couldn't find user %s.",nickp);
596 return;
597 }
598
599 if (!IsAccount(bun)) {
600 sendnoticetouser(me,np,"%s is not authed.",nickp);
601 return;
602 }
603
604 bptr=bun->authname;
605 }
606
607 bu=(blockeduser *)malloc(sizeof(blockeduser));
608 bu->type=0;
609 strncpy(bu->content,bptr,ACCOUNTLEN);
610
611 bu->next=blockedusers;
612 blockedusers=bu;
613
614 sendnoticetouser(me,np,"Now blocking all messages from users with accountname %s.",bptr);
615 return;
616 } else {
617 /* block the user by a hostmask */
618 char *textc;
619 blockeduser *bu;
620
621 textc=text+7;
622
623 if (!strchr(textc,'@') || !strchr(textc,'!')) {
624 /* not a valid hostmask */
625 sendnoticetouser(me,np,"%s is not a valid hostmask.",textc);
626 return;
627 }
628
629 bu=(blockeduser *)malloc(sizeof(blockeduser));
630 bu->type=1;
631 strncpy(bu->content,textc,NICKLEN+USERLEN+HOSTLEN+2);
632
633 bu->next=blockedusers;
634 blockedusers=bu;
635
636 sendnoticetouser(me,np,"Now blocking all messages from users with a hostmask matching %s.",textc);
637 }
638 } else if (!ircd_strncmp(text, "!answer ", 8)) {
639 /* Answering a question */
640 int qid;
641 char *ch=text+7;
642 channel *pcp;
643 questrec *qrp;
644
645 /* answers will no be stored and sent later on if mic is enabled */
646
647 while (*(++ch) == ' '); /* skip spaces */
648
649 qid=strtol(ch, &ch, 10);
650
651 if (!*ch) {
652 sendnoticetouser(me,np,"No answer supplied.");
653 return;
654 }
655
656 while (*(++ch) == ' '); /* skip spaces */
657
658 if (!*ch) {
659 sendnoticetouser(me,np,"No answer supplied.");
660 return;
661 }
662
663 if (qid < 1 || qid > lastquestionID) {
664 sendnoticetouser(me,np,"Invalid question ID %d.",qid);
665 return;
666 }
667
668 for (qrp=questiontable[qid%QUESTIONHASHSIZE];qrp;qrp=qrp->next)
669 if (qrp->ID==qid)
670 break;
671
672 if (!qrp) {
673 sendnoticetouser(me,np,"Can't find question %d.",qid);
674 return;
675 }
676
677 switch(qrp->flags & QUESTION_STATE) {
678 case QUESTION_ANSWERED:
679 sendnoticetouser(me,np,"Question %d has already been answered.",qid);
680 return;
681
682 case QUESTION_OFFTOPIC:
683 sendnoticetouser(me,np,"Question %d has been marked off-topic.",qid);
684 return;
685
686 case QUESTION_SPAM:
687 sendnoticetouser(me,np,"Question %d has been marked as spam.",qid);
688 return;
689
690 default:
691 break;
692 }
693
694 qrp->flags = ((qrp->flags) & ~QUESTION_STATE) | QUESTION_ANSWERED;
695 qrp->answer=getsstring(ch, 250);
696
697 if ((pcp=findchannel(PUBLICCHAN)) && (!nextspam) && (!micnumeric)) {
698 sendmessagetochannel(me, pcp, "%s asked: %s",qrp->nick,qrp->question->content);
699 sendmessagetochannel(me, pcp, "%s answers: %s",np->nick,ch);
700 } else {
701 storedanswer *ans;
702 ans=malloc(sizeof(storedanswer));
703 ans->qptr=qrp;
704 strncpy(ans->nick,np->nick,NICKLEN);
705
706 ans->next=storedanswers;
707 storedanswers=ans;
708
709 sendnoticetouser(me,np,"Can't send your answer right now. Answer was stored and will be sent later on.");
710 return;
711 }
712
713 sendnoticetouser(me,np,"Answer to question %d has been sent and stored.",qid);
714 }
715 }
716 } else if (type==LU_PRIVMSG || type==LU_SECUREMSG) {
717 np=args[0];
718 text=args[1];
719
720 lastmsg=(time_t)np->exts[tutornext];
721
722 if (lastmsg + QUESTIONINTERVAL > time(NULL)) {
723 sendnoticetouser(me, np, "You have already sent a question recently - please wait at least 30 seconds between asking questions");
724 } else {
725 char hostbuf[HOSTLEN+USERLEN+NICKLEN+3];
726 questrec *qrp;
727 blockeduser *bu;
728 channel *cp;
729
730 sendnoticetouser(me, np, "Your question has been relayed to the #tutorial staff.");
731
732 cp=findchannel(PUBLICCHAN);
733
734 if (!getnumerichandlefromchanhash(cp->users, np->numeric))
735 return;
736
737 if ((strlen(text)<5) || (!strchr(text,' ')) || (*text==1))
738 return;
739
740 sprintf(hostbuf,"%s!%s@%s",np->nick,np->ident,np->host->name->content);
741
742 for(bu=blockedusers;bu;bu=bu->next) {
743 if (bu->type) {
744 /* user@host check */
745 if (!match(bu->content,hostbuf))
746 return;
747 } else {
748 /* account name check */
749 if ((IsAccount(np)) && !ircd_strncmp(np->authname,bu->content,ACCOUNTLEN))
750 return;
751 }
752 }
753
754 np->exts[tutornext] = (void *)time(NULL);
755
756 qrp=(questrec *)malloc(sizeof(questrec));
757
758 qrp->ID=++lastquestionID;
759 qrp->question=getsstring(text,250);
760 qrp->flags=QUESTION_NEW;
761 strcpy(qrp->nick, np->nick);
762 qrp->numeric=np->numeric;
763 qrp->answer=NULL;
764 qrp->next=questiontable[qrp->ID % QUESTIONHASHSIZE];
765
766 questiontable[qrp->ID % QUESTIONHASHSIZE] = qrp;
767
768 if ((cp=findchannel(QUESTIONCHAN))) {
769 sendmessagetochannel(me, cp, "ID: %3d <%s> %s", qrp->ID, visiblehostmask(np, hostbuf), text);
770
771 /* Send a seperator every 10 questions */
772 if ((lastquestionID % 10)==0)
773 sendmessagetochannel(me,cp,"-----------------------------------");
774 }
775 }
776 } else if (type==LU_KILLED) {
777 tutornick=NULL;
778 } else if (type==LU_INVITE) {
779 /* we were invited, check if someone invited us to PUBLICCHAN */
780 cp=args[1];
781 if (!ircd_strcmp(cp->index->name->content,PUBLICCHAN)) {
782 localjoinchannel(me,cp);
783 localgetops(me,cp);
784 }
785 return;
786 }
787 }