]>
Commit | Line | Data |
---|---|---|
c86edd1d Q |
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" | |
5857b2db P |
8 | #include "../lib/version.h" |
9 | ||
10 | MODULE_VERSION(""); | |
c86edd1d Q |
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))) { | |
ecdd6f2c | 80 | sendmessagetochannel(tutornick, cp, "%s", nextspam->message->content); |
c86edd1d Q |
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))) { | |
ecdd6f2c | 336 | sendmessagetochannel(me, cp, "%s", text); |
c86edd1d Q |
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 | } |