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