-#include <stdio.h>\r
-#include <stdlib.h>\r
-#include <stdarg.h>\r
-#include <string.h>\r
-#include <time.h>\r
-\r
-#include "../nick/nick.h"\r
-#include "../localuser/localuserchannel.h"\r
-#include "../core/hooks.h"\r
-#include "../core/schedule.h"\r
-#include "../lib/array.h"\r
-#include "../lib/base64.h"\r
-#include "../lib/irc_string.h"\r
-#include "../lib/splitline.h"\r
-\r
-#include "qabot.h"\r
-\r
-time_t qab_startime;\r
-int qabot_nickext;\r
-int qabot_spam_nickext;\r
-int qabot_chanext;\r
-nick* qabot_nick = 0;\r
-CommandTree* qabot_commands;\r
-CommandTree* qabot_chancommands;\r
-qab_bot* qab_bots = 0;\r
-unsigned long qab_lastq_crc = 0;\r
-int qab_lastq_count = 0;\r
-qab_bot* qabot_currentbot = 0;\r
-channel* qabot_currentchan = 0;\r
-\r
-void _init() {\r
- qab_startime = time(0);\r
- \r
- registerhook(HOOK_NICK_LOSTNICK, qabot_lostnick);\r
- registerhook(HOOK_CHANNEL_PART, qabot_channel_part);\r
- \r
- qabot_commands = newcommandtree();\r
- addcommandtotree(qabot_commands, "showcommands", 0, 0, qabot_doshowcommands);\r
- addcommandtotree(qabot_commands, "help", QAUFLAG_STAFF, 1, qabot_dohelp);\r
- addcommandtotree(qabot_commands, "hello", 0, 0, qabot_dohello);\r
- addcommandtotree(qabot_commands, "save", QAUFLAG_ADMIN, 0, qabot_dosave);\r
- addcommandtotree(qabot_commands, "listbots", QAUFLAG_STAFF, 0, qabot_dolistbots);\r
- addcommandtotree(qabot_commands, "listusers", QAUFLAG_STAFF, 0, qabot_dolistusers);\r
- addcommandtotree(qabot_commands, "showbot", QAUFLAG_STAFF, 1, qabot_doshowbot);\r
- addcommandtotree(qabot_commands, "addbot", QAUFLAG_STAFF, 6, qabot_doaddbot);\r
- addcommandtotree(qabot_commands, "delbot", QAUFLAG_STAFF, 1, qabot_dodelbot);\r
- addcommandtotree(qabot_commands, "adduser", QAUFLAG_ADMIN, 2, qabot_doadduser);\r
- addcommandtotree(qabot_commands, "changelev", QAUFLAG_ADMIN, 2, qabot_dochangelev);\r
- addcommandtotree(qabot_commands, "deluser", QAUFLAG_ADMIN, 1, qabot_dodeluser);\r
- addcommandtotree(qabot_commands, "whois", QAUFLAG_STAFF, 1, qabot_dowhois);\r
- addcommandtotree(qabot_commands, "status", QAUFLAG_DEVELOPER, 0, qabot_dostatus);\r
- \r
- qabot_chancommands = newcommandtree();\r
- addcommandtotree(qabot_chancommands, "answer", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 2, qabot_dochananswer);\r
- addcommandtotree(qabot_chancommands, "block", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 2, qabot_dochanblock);\r
- addcommandtotree(qabot_chancommands, "clear", QAC_STAFFCHAN, 0, qabot_dochanclear);\r
- addcommandtotree(qabot_chancommands, "closechan", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 0, qabot_dochanclosechan);\r
- addcommandtotree(qabot_chancommands, "config", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 2, qabot_dochanconfig);\r
- addcommandtotree(qabot_chancommands, "help", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 1, qabot_dochanhelp);\r
- addcommandtotree(qabot_chancommands, "listblocks", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 0, qabot_dochanlistblocks);\r
- addcommandtotree(qabot_chancommands, "mic", QAC_STAFFCHAN, 0, qabot_dochanmic);\r
- addcommandtotree(qabot_chancommands, "moo", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 0, qabot_dochanmoo);\r
- addcommandtotree(qabot_chancommands, "offtopic", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 30, qabot_dochanofftopic);\r
- addcommandtotree(qabot_chancommands, "openchan", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 0, qabot_dochanopenchan);\r
- addcommandtotree(qabot_chancommands, "ping", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 0, qabot_dochanping);\r
- addcommandtotree(qabot_chancommands, "reset", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 1, qabot_dochanreset);\r
- addcommandtotree(qabot_chancommands, "spam", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 30, qabot_dochanspam);\r
- addcommandtotree(qabot_chancommands, "status", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 0, qabot_dochanstatus);\r
- addcommandtotree(qabot_chancommands, "unblock", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 2, qabot_dochanunblock);\r
-\r
- if ((qabot_nickext = registernickext("QABOT")) == -1) {\r
- return;\r
- }\r
- \r
- if ((qabot_spam_nickext = registernickext("QABOT_SPAM")) == -1) {\r
- return;\r
- }\r
- \r
- qabot_loaddb();\r
-\r
- scheduleoneshot(time(NULL) + 1, &qabot_createfakeuser, NULL);\r
- scheduleoneshot(time(NULL) + QABOT_SAVEWAIT, &qabot_savetimer, NULL);\r
-}\r
-\r
-void _fini() {\r
- deregisterhook(HOOK_NICK_LOSTNICK, qabot_lostnick);\r
- deregisterhook(HOOK_CHANNEL_PART, qabot_channel_part);\r
- \r
- deleteschedule(0, qabot_savetimer, NULL);\r
- \r
- qabot_savedb();\r
- \r
- while (qab_bots)\r
- qabot_delbot(qab_bots);\r
-\r
- if (qabot_nickext != -1)\r
- releasenickext(qabot_nickext);\r
- \r
- if (qabot_spam_nickext != -1)\r
- releasenickext(qabot_spam_nickext);\r
-\r
- if (qabot_nick) {\r
- deregisterlocaluser(qabot_nick, "Module unloaded.");\r
- qabot_nick = NULL;\r
- }\r
-\r
- while (qabot_users)\r
- qabot_squelchuser(qabot_users);\r
- \r
- deletecommandfromtree(qabot_commands, "showcommands", qabot_doshowcommands);\r
- deletecommandfromtree(qabot_commands, "help", qabot_dohelp);\r
- deletecommandfromtree(qabot_commands, "hello", qabot_dohello);\r
- deletecommandfromtree(qabot_commands, "save", qabot_dosave);\r
- deletecommandfromtree(qabot_commands, "listbots", qabot_dolistbots);\r
- deletecommandfromtree(qabot_commands, "listusers", qabot_dolistusers);\r
- deletecommandfromtree(qabot_commands, "showbot", qabot_doshowbot);\r
- deletecommandfromtree(qabot_commands, "addbot", qabot_doaddbot);\r
- deletecommandfromtree(qabot_commands, "delbot", qabot_dodelbot);\r
- deletecommandfromtree(qabot_commands, "adduser", qabot_doadduser);\r
- deletecommandfromtree(qabot_commands, "changelev", qabot_dochangelev);\r
- deletecommandfromtree(qabot_commands, "deluser", qabot_dodeluser);\r
- deletecommandfromtree(qabot_commands, "whois", qabot_dowhois);\r
- deletecommandfromtree(qabot_commands, "status", qabot_dostatus);\r
- destroycommandtree(qabot_commands);\r
- \r
- deletecommandfromtree(qabot_chancommands, "answer", qabot_dochananswer);\r
- deletecommandfromtree(qabot_chancommands, "block", qabot_dochanblock);\r
- deletecommandfromtree(qabot_chancommands, "clear", qabot_dochanclear);\r
- deletecommandfromtree(qabot_chancommands, "closechan", qabot_dochanclosechan);\r
- deletecommandfromtree(qabot_chancommands, "config", qabot_dochanconfig);\r
- deletecommandfromtree(qabot_chancommands, "help", qabot_dochanhelp);\r
- deletecommandfromtree(qabot_chancommands, "listblocks", qabot_dochanlistblocks);\r
- deletecommandfromtree(qabot_chancommands, "mic", qabot_dochanmic);\r
- deletecommandfromtree(qabot_chancommands, "moo", qabot_dochanmoo);\r
- deletecommandfromtree(qabot_chancommands, "offtopic", qabot_dochanofftopic);\r
- deletecommandfromtree(qabot_chancommands, "openchan", qabot_dochanopenchan);\r
- deletecommandfromtree(qabot_chancommands, "ping", qabot_dochanping);\r
- deletecommandfromtree(qabot_chancommands, "reset", qabot_dochanreset);\r
- deletecommandfromtree(qabot_chancommands, "spam", qabot_dochanspam);\r
- deletecommandfromtree(qabot_chancommands, "status", qabot_dochanstatus);\r
- deletecommandfromtree(qabot_chancommands, "unblock", qabot_dochanunblock);\r
- destroycommandtree(qabot_chancommands);\r
-}\r
-\r
-void qabot_lostnick(int hooknum, void* arg) {\r
- nick* np = (nick*)arg;\r
- qab_bot* b;\r
- \r
- if (!qab_bots)\r
- return;\r
- \r
- if (!IsAccount(np))\r
- return;\r
- \r
- for (b = qab_bots; b; b = b->next) {\r
- if (b->micnumeric == np->numeric) {\r
- b->micnumeric = 0;\r
- sendmessagetochannel(b->np, b->staff_chan->channel, "Mic deactivated.");\r
- if (!b->lastspam)\r
- qabot_spamstored((void*)b);\r
- }\r
- }\r
-}\r
-\r
-void qabot_channel_part(int hooknum, void* arg) {\r
- channel* cp = (channel*)((void**)arg)[0];\r
- nick* np = (nick*)((void**)arg)[1];\r
- qab_bot* b;\r
- \r
- if (!qab_bots)\r
- return;\r
- \r
- if (!IsAccount(np))\r
- return;\r
- \r
- for (b = qab_bots; b; b = b->next) {\r
- if ((b->micnumeric == np->numeric) && (b->staff_chan->channel == cp)) {\r
- b->micnumeric = 0;\r
- sendmessagetochannel(b->np, b->staff_chan->channel, "Mic deactivated.");\r
- if (!b->lastspam)\r
- qabot_spamstored((void*)b);\r
- }\r
- }\r
-}\r
-\r
-void qabot_createfakeuser(void* arg) {\r
- channel* cp;\r
- \r
- if ((qabot_nick = registerlocaluser(QABOT_NICK, QABOT_USER, QABOT_HOST,\r
- QABOT_NAME, QABOT_ACCT, QABOT_UMDE, &qabot_handler))) {\r
- if ((cp = findchannel(QABOT_HOMECHAN))) {\r
- localjoinchannel(qabot_nick, cp);\r
- localgetops(qabot_nick, cp);\r
- }\r
- else\r
- localcreatechannel(qabot_nick, QABOT_HOMECHAN);\r
- }\r
-}\r
-\r
-void qabot_handler(nick* me, int type, void** args) {\r
- nick* sender;\r
- Command* cmd;\r
- channel* cp;\r
- char* cargv[50];\r
- int cargc;\r
- qab_user* u;\r
-\r
- switch (type) {\r
- case LU_PRIVMSG:\r
- case LU_SECUREMSG:\r
- sender = (nick*)args[0];\r
-\r
- if (!strncmp("\001VERSION", args[1], 8)) {\r
- sendnoticetouser(qabot_nick, sender, "\001VERSION %s\001", QABOT_NAME);\r
- return;\r
- }\r
-\r
- cargc = splitline((char*)args[1], cargv, 50, 0);\r
- cmd = findcommandintree(qabot_commands, cargv[0], 1);\r
- if (!cmd) {\r
- sendnoticetouser(qabot_nick, sender, "Unknown command.");\r
- return;\r
- }\r
-\r
- if (!IsAccount(sender)) {\r
- sendnoticetouser(qabot_nick, sender, "You must be authed to use this command.");\r
- return;\r
- }\r
-\r
- u = qabot_getuser(sender->authname);\r
- if (cmd->level) {\r
- if (!u) {\r
- sendnoticetouser(qabot_nick, sender, "You need an account to use this command.");\r
- return;\r
- }\r
-\r
- if ((cmd->level & QAUFLAG_STAFF) && !QAIsStaff(u)) {\r
- sendnoticetouser(qabot_nick, sender, "You do not have access to this command.");\r
- return;\r
- }\r
-\r
- if ((cmd->level & QAUFLAG_ADMIN) && !QAIsAdmin(u)) {\r
- sendnoticetouser(qabot_nick, sender, "You do not have access to this command.");\r
- return;\r
- }\r
- \r
- if ((cmd->level & QAUFLAG_DEVELOPER) && !QAIsDeveloper(u)) {\r
- sendnoticetouser(qabot_nick, sender, "You do not have access to this command.");\r
- return;\r
- }\r
- }\r
- \r
- if (cmd->maxparams < (cargc-1)) {\r
- rejoinline(cargv[cmd->maxparams], cargc - (cmd->maxparams));\r
- cargc=(cmd->maxparams) + 1;\r
- }\r
- \r
- (cmd->handler)((void*)sender, cargc - 1, &(cargv[1]));\r
-\r
- break;\r
-\r
- case LU_KILLED:\r
- qabot_nick = NULL;\r
- scheduleoneshot(time(NULL) + 1, &qabot_createfakeuser, NULL);\r
- break;\r
-\r
- case LU_KICKED:\r
- cp = (channel*)args[1];\r
- if (cp) {\r
- localjoinchannel(qabot_nick, cp);\r
- localgetops(qabot_nick, cp);\r
- }\r
- break;\r
- }\r
-}\r
-\r
-void qabot_child_handler(nick* me, int type, void** args) {\r
- nick* sender;\r
- channel* cp;\r
- qab_bot* bot = me->exts[qabot_nickext];\r
- char* text;\r
- Command* cmd;\r
- char* cargv[50];\r
- int cargc;\r
- \r
- if (!bot) {\r
- return;\r
- }\r
- \r
- switch (type) {\r
- case LU_CHANMSG:\r
- sender = (nick*)args[0];\r
- cp = (channel*)args[1];\r
- text = (char*)args[2];\r
- \r
- if (*text == '!') {\r
- cargc = splitline((char*)(++text), cargv, 50, 0);\r
- cmd = findcommandintree(qabot_chancommands, cargv[0], 1);\r
- if (!cmd) {\r
- sendnoticetouser(me, sender, "Unknown command.");\r
- return;\r
- }\r
- \r
- if ((cp->index == bot->staff_chan) && !(cmd->level & QAC_STAFFCHAN)) {\r
- sendnoticetouser(me, sender, "This command cannot be used in the staff channel.");\r
- return;\r
- }\r
- \r
- if ((cp->index == bot->question_chan) && !(cmd->level & QAC_QUESTIONCHAN)) {\r
- sendnoticetouser(me, sender, "This command cannot be used in the question channel.");\r
- return;\r
- }\r
- \r
- if (cmd->maxparams < (cargc-1)) {\r
- rejoinline(cargv[cmd->maxparams], cargc - (cmd->maxparams));\r
- cargc=(cmd->maxparams) + 1;\r
- }\r
- \r
- qabot_currentbot = bot;\r
- qabot_currentchan = cp;\r
- \r
- (cmd->handler)((void*)sender, cargc - 1, &(cargv[1]));\r
- }\r
- else if ((*text != '!') && (bot->micnumeric == sender->numeric) && (cp->index == bot->staff_chan)) {\r
- bot->lastmic = time(NULL);\r
- if (bot->lastspam) {\r
- qab_spam* s;\r
- \r
- s = (qab_spam*)malloc(sizeof(qab_spam));\r
- s->message = strdup(text);\r
- s->next = 0;\r
- bot->lastspam->next = s;\r
- bot->lastspam = s;\r
- }\r
- else {\r
- if ((bot->spamtime + bot->spam_interval) < time(0)) {\r
- sendmessagetochannel(me, bot->public_chan->channel, "%s", text);\r
- bot->spammed++;\r
- bot->spamtime = time(0);\r
- }\r
- else {\r
- qab_spam* s;\r
- \r
- s = (qab_spam*)malloc(sizeof(qab_spam));\r
- s->message = strdup(text);\r
- s->next = 0;\r
- bot->nextspam = bot->lastspam = s;\r
- scheduleoneshot(bot->spamtime + bot->spam_interval, qabot_spam, (void*)bot);\r
- }\r
- }\r
- }\r
- break;\r
- \r
- case LU_PRIVMSG:\r
- case LU_SECUREMSG:\r
- sender = (nick*)args[0];\r
- text = (char*)args[1];\r
- time_t lastmsg;\r
- \r
- lastmsg = (time_t)sender->exts[qabot_spam_nickext];\r
- \r
- if ((lastmsg + bot->ask_wait) > time(0)) {\r
- sendnoticetouser(me, sender, "You have already sent a question recently, please wait at least %d seconds between asking questions.", bot->ask_wait);\r
- return;\r
- }\r
- else {\r
- char hostbuf[NICKLEN + USERLEN + HOSTLEN + 3];\r
- qab_block* b;\r
- qab_question* q;\r
- unsigned long crc;\r
- int len;\r
- \r
- sendnoticetouser(me, sender, "Your question has been relayed to the %s staff.", bot->public_chan->name->content);\r
- \r
- if (!getnumerichandlefromchanhash(bot->public_chan->channel->users, sender->numeric))\r
- return;\r
- \r
- if (*text == '<')\r
- text++;\r
- \r
- len = strlen(text);\r
- if ((len > 0) && (text[len - 1] == '>')) {\r
- text[len - 1] = '\0';\r
- len--;\r
- }\r
- \r
- if ((len < 5) || !strchr(text, ' ') || (*text == 1))\r
- return;\r
- \r
- if ((bot->flags & QAB_AUTHEDONLY) && !IsAccount(sender))\r
- return;\r
- \r
- if (bot->flags & QAB_CONTROLCHAR) {\r
- char* ch;\r
- \r
- for (ch = text; *ch; ch++)\r
- if ((*ch == 2) || (*ch == 3) || (*ch == 22) || (*ch == 27) || (*ch == 31))\r
- return;\r
- }\r
- else if (bot->flags & QAB_COLOUR) {\r
- char* ch;\r
- \r
- for (ch = text; *ch; ch++)\r
- if (*ch == 3)\r
- return;\r
- }\r
- \r
- if (bot->flags & QAB_FLOODDETECT) {\r
- crc = crc32i(text);\r
- if (crc == qab_lastq_crc) {\r
- qab_lastq_count++;\r
- if (qab_lastq_count >= 3) {\r
- if ((qab_lastq_count == 3) || ((qab_lastq_count % 10) == 0))\r
- sendmessagetochannel(me, bot->question_chan->channel, "WARNING: Possible question flood detected%s", (bot->flags & QAB_FLOODSTOP) ? " - AUTO IGNORED." : ".");\r
- if (bot->flags & QAB_FLOODSTOP)\r
- return;\r
- }\r
- }\r
- else {\r
- qab_lastq_crc = crc;\r
- qab_lastq_count = 1;\r
- }\r
- }\r
- \r
- sprintf(hostbuf,"%s!%s@%s", sender->nick, sender->ident, sender->host->name->content);\r
- \r
- for (b = bot->blocks; b; b = b->next) {\r
- switch (b->type) {\r
- case QABBLOCK_ACCOUNT:\r
- if (IsAccount(sender) && !ircd_strncmp(sender->authname, b->blockstr, ACCOUNTLEN))\r
- return;\r
- break;\r
- \r
- case QABBLOCK_HOST:\r
- if (!match(b->blockstr, hostbuf))\r
- return;\r
- break;\r
- \r
- case QABBLOCK_TEXT:\r
- if (!match(b->blockstr, text))\r
- return;\r
- break;\r
- }\r
- }\r
- \r
- sender->exts[qabot_spam_nickext] = (void*)time(0);\r
- \r
- q = (qab_question*)malloc(sizeof(qab_question));\r
- q->id = ++bot->lastquestionID;\r
- q->question = strdup(text);\r
- q->flags = QAQ_NEW;\r
- strncpy(q->nick, sender->nick, NICKLEN);\r
- q->nick[NICKLEN] = '\0';\r
- q->numeric = sender->numeric;\r
- q->crc = (bot->flags & QAB_FLOODDETECT) ? crc : 0;\r
- q->answer = 0;\r
- q->next = bot->questions[q->id % QUESTIONHASHSIZE];\r
- \r
- bot->questions[q->id % QUESTIONHASHSIZE] = q;\r
- \r
- sendmessagetochannel(me, bot->question_chan->channel, "ID: %3d <%s> %s", q->id, visiblehostmask(sender, hostbuf), text);\r
- \r
- if ((bot->flags & QAB_LINEBREAK) && ((bot->lastquestionID % 10) == 0))\r
- sendmessagetochannel(me, bot->question_chan->channel, "-----------------------------------");\r
- }\r
- break;\r
- \r
- case LU_KILLED:\r
- bot->np = NULL;\r
- scheduleoneshot(time(NULL) + 1, &qabot_createbotfakeuser, (void*)bot);\r
- break;\r
-\r
- case LU_KICKED:\r
- cp = (channel*)args[1];\r
- if (cp) {\r
- localjoinchannel(bot->np, cp);\r
- localgetops(bot->np, cp);\r
- }\r
- break;\r
- \r
- default:\r
- break;\r
- }\r
-}\r
-\r
-char* qabot_getvictim(nick* np, char* target) {\r
- if (*target != '#') {\r
- nick* victim;\r
- \r
- if (!(victim = getnickbynick(target))) {\r
- sendnoticetouser(qabot_nick, np, "No such nickname.");\r
- return 0;\r
- }\r
- \r
- if (!IsAccount(victim)) {\r
- sendnoticetouser(qabot_nick, np, "%s is not authed.", victim->nick);\r
- return 0;\r
- }\r
- \r
- return victim->authname;\r
- }\r
- \r
- return target + 1;\r
-}\r
-\r
-const char* qabot_uflagtostr(flag_t flags) {\r
- static char buf[20];\r
- int i = 0;\r
- \r
- buf[i++] = '+';\r
- \r
- if (flags & QAUFLAG_ADMIN) { buf[i++] = 'a'; }\r
- if (flags & QAUFLAG_DEVELOPER) { buf[i++] = 'd'; }\r
- if (flags & QAUFLAG_STAFF) { buf[i++] = 's'; }\r
- \r
- buf[i] = '\0';\r
- \r
- return buf;\r
-}\r
-\r
-const char* qabot_formattime(time_t tme) {\r
- static char buf[50];\r
- struct tm* tmp;\r
- \r
- tmp = gmtime(&tme);\r
- strftime(buf, 15, "%d/%m/%y %H:%M", tmp);\r
- \r
- return buf;\r
-}\r
-\r
-qab_bot* qabot_getcurrentbot() {\r
- return qabot_currentbot;\r
-}\r
-\r
-channel* qabot_getcurrentchannel() {\r
- return qabot_currentchan;\r
-}\r
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <time.h>
+
+#include "../nick/nick.h"
+#include "../localuser/localuserchannel.h"
+#include "../core/hooks.h"
+#include "../core/schedule.h"
+#include "../lib/array.h"
+#include "../lib/base64.h"
+#include "../lib/irc_string.h"
+#include "../lib/splitline.h"
+#include "../lib/version.h"
+
+#include "qabot.h"
+
+MODULE_VERSION("");
+
+time_t qab_startime;
+int qabot_nickext;
+int qabot_spam_nickext;
+int qabot_chanext;
+nick* qabot_nick = 0;
+CommandTree* qabot_commands;
+CommandTree* qabot_chancommands;
+qab_bot* qab_bots = 0;
+unsigned long qab_lastq_crc = 0;
+int qab_lastq_count = 0;
+qab_bot* qabot_currentbot = 0;
+channel* qabot_currentchan = 0;
+
+void _init() {
+ qab_startime = time(0);
+
+ registerhook(HOOK_NICK_LOSTNICK, qabot_lostnick);
+ registerhook(HOOK_CHANNEL_PART, qabot_channel_part);
+
+ qabot_commands = newcommandtree();
+ addcommandtotree(qabot_commands, "showcommands", 0, 0, qabot_doshowcommands);
+ addcommandtotree(qabot_commands, "help", QAUFLAG_STAFF, 1, qabot_dohelp);
+ addcommandtotree(qabot_commands, "hello", 0, 0, qabot_dohello);
+ addcommandtotree(qabot_commands, "save", QAUFLAG_ADMIN, 0, qabot_dosave);
+ addcommandtotree(qabot_commands, "listbots", QAUFLAG_STAFF, 0, qabot_dolistbots);
+ addcommandtotree(qabot_commands, "listusers", QAUFLAG_STAFF, 0, qabot_dolistusers);
+ addcommandtotree(qabot_commands, "showbot", QAUFLAG_STAFF, 1, qabot_doshowbot);
+ addcommandtotree(qabot_commands, "addbot", QAUFLAG_STAFF, 6, qabot_doaddbot);
+ addcommandtotree(qabot_commands, "delbot", QAUFLAG_STAFF, 1, qabot_dodelbot);
+ addcommandtotree(qabot_commands, "adduser", QAUFLAG_ADMIN, 2, qabot_doadduser);
+ addcommandtotree(qabot_commands, "changelev", QAUFLAG_ADMIN, 2, qabot_dochangelev);
+ addcommandtotree(qabot_commands, "deluser", QAUFLAG_ADMIN, 1, qabot_dodeluser);
+ addcommandtotree(qabot_commands, "whois", QAUFLAG_STAFF, 1, qabot_dowhois);
+ addcommandtotree(qabot_commands, "status", QAUFLAG_DEVELOPER, 0, qabot_dostatus);
+
+ qabot_chancommands = newcommandtree();
+ addcommandtotree(qabot_chancommands, "answer", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 2, qabot_dochananswer);
+ addcommandtotree(qabot_chancommands, "block", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 2, qabot_dochanblock);
+ addcommandtotree(qabot_chancommands, "clear", QAC_STAFFCHAN, 0, qabot_dochanclear);
+ addcommandtotree(qabot_chancommands, "closechan", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 0, qabot_dochanclosechan);
+ addcommandtotree(qabot_chancommands, "config", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 2, qabot_dochanconfig);
+ addcommandtotree(qabot_chancommands, "help", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 1, qabot_dochanhelp);
+ addcommandtotree(qabot_chancommands, "listblocks", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 0, qabot_dochanlistblocks);
+ addcommandtotree(qabot_chancommands, "mic", QAC_STAFFCHAN, 0, qabot_dochanmic);
+ addcommandtotree(qabot_chancommands, "moo", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 0, qabot_dochanmoo);
+ addcommandtotree(qabot_chancommands, "offtopic", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 30, qabot_dochanofftopic);
+ addcommandtotree(qabot_chancommands, "openchan", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 0, qabot_dochanopenchan);
+ addcommandtotree(qabot_chancommands, "ping", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 0, qabot_dochanping);
+ addcommandtotree(qabot_chancommands, "reset", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 1, qabot_dochanreset);
+ addcommandtotree(qabot_chancommands, "spam", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 30, qabot_dochanspam);
+ addcommandtotree(qabot_chancommands, "status", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 0, qabot_dochanstatus);
+ addcommandtotree(qabot_chancommands, "unblock", QAC_QUESTIONCHAN|QAC_STAFFCHAN, 2, qabot_dochanunblock);
+ addcommandtotree(qabot_chancommands, "record", QAC_STAFFCHAN, 1, qabot_dochanrecord);
+ addcommandtotree(qabot_chancommands, "play", QAC_STAFFCHAN, 1, qabot_dochanplay);
+ addcommandtotree(qabot_chancommands, "continue", QAC_STAFFCHAN, 0, qabot_dochancontinue);
+ addcommandtotree(qabot_chancommands, "stop", QAC_STAFFCHAN, 0, qabot_dochanstop);
+ addcommandtotree(qabot_chancommands, "delete", QAC_STAFFCHAN, 1, qabot_dochandelete);
+ addcommandtotree(qabot_chancommands, "list", QAC_STAFFCHAN, 0, qabot_dochanlist);
+
+ if ((qabot_nickext = registernickext("QABOT")) == -1) {
+ return;
+ }
+
+ if ((qabot_spam_nickext = registernickext("QABOT_SPAM")) == -1) {
+ return;
+ }
+
+ qabot_loaddb();
+
+ scheduleoneshot(time(NULL) + 1, &qabot_createfakeuser, NULL);
+ scheduleoneshot(time(NULL) + QABOT_SAVEWAIT, &qabot_savetimer, NULL);
+}
+
+void _fini() {
+ deregisterhook(HOOK_NICK_LOSTNICK, qabot_lostnick);
+ deregisterhook(HOOK_CHANNEL_PART, qabot_channel_part);
+
+ deleteschedule(0, qabot_savetimer, NULL);
+
+ qabot_savedb();
+
+ while (qab_bots)
+ qabot_delbot(qab_bots);
+
+ if (qabot_nickext != -1)
+ releasenickext(qabot_nickext);
+
+ if (qabot_spam_nickext != -1)
+ releasenickext(qabot_spam_nickext);
+
+ if (qabot_nick) {
+ deregisterlocaluser(qabot_nick, "Module unloaded.");
+ qabot_nick = NULL;
+ }
+
+ while (qabot_users)
+ qabot_squelchuser(qabot_users);
+
+ deletecommandfromtree(qabot_commands, "showcommands", qabot_doshowcommands);
+ deletecommandfromtree(qabot_commands, "help", qabot_dohelp);
+ deletecommandfromtree(qabot_commands, "hello", qabot_dohello);
+ deletecommandfromtree(qabot_commands, "save", qabot_dosave);
+ deletecommandfromtree(qabot_commands, "listbots", qabot_dolistbots);
+ deletecommandfromtree(qabot_commands, "listusers", qabot_dolistusers);
+ deletecommandfromtree(qabot_commands, "showbot", qabot_doshowbot);
+ deletecommandfromtree(qabot_commands, "addbot", qabot_doaddbot);
+ deletecommandfromtree(qabot_commands, "delbot", qabot_dodelbot);
+ deletecommandfromtree(qabot_commands, "adduser", qabot_doadduser);
+ deletecommandfromtree(qabot_commands, "changelev", qabot_dochangelev);
+ deletecommandfromtree(qabot_commands, "deluser", qabot_dodeluser);
+ deletecommandfromtree(qabot_commands, "whois", qabot_dowhois);
+ deletecommandfromtree(qabot_commands, "status", qabot_dostatus);
+ destroycommandtree(qabot_commands);
+
+ deletecommandfromtree(qabot_chancommands, "answer", qabot_dochananswer);
+ deletecommandfromtree(qabot_chancommands, "block", qabot_dochanblock);
+ deletecommandfromtree(qabot_chancommands, "clear", qabot_dochanclear);
+ deletecommandfromtree(qabot_chancommands, "closechan", qabot_dochanclosechan);
+ deletecommandfromtree(qabot_chancommands, "config", qabot_dochanconfig);
+ deletecommandfromtree(qabot_chancommands, "help", qabot_dochanhelp);
+ deletecommandfromtree(qabot_chancommands, "listblocks", qabot_dochanlistblocks);
+ deletecommandfromtree(qabot_chancommands, "mic", qabot_dochanmic);
+ deletecommandfromtree(qabot_chancommands, "moo", qabot_dochanmoo);
+ deletecommandfromtree(qabot_chancommands, "offtopic", qabot_dochanofftopic);
+ deletecommandfromtree(qabot_chancommands, "openchan", qabot_dochanopenchan);
+ deletecommandfromtree(qabot_chancommands, "ping", qabot_dochanping);
+ deletecommandfromtree(qabot_chancommands, "reset", qabot_dochanreset);
+ deletecommandfromtree(qabot_chancommands, "spam", qabot_dochanspam);
+ deletecommandfromtree(qabot_chancommands, "status", qabot_dochanstatus);
+ deletecommandfromtree(qabot_chancommands, "unblock", qabot_dochanunblock);
+ deletecommandfromtree(qabot_chancommands, "record", qabot_dochanrecord);
+ deletecommandfromtree(qabot_chancommands, "play", qabot_dochanplay);
+ deletecommandfromtree(qabot_chancommands, "continue", qabot_dochancontinue);
+ deletecommandfromtree(qabot_chancommands, "stop", qabot_dochanstop);
+ deletecommandfromtree(qabot_chancommands, "delete", qabot_dochandelete);
+ deletecommandfromtree(qabot_chancommands, "list", qabot_dochanlist);
+ destroycommandtree(qabot_chancommands);
+}
+
+void qabot_lostnick(int hooknum, void* arg) {
+ nick* np = (nick*)arg;
+ qab_bot* b;
+
+ if (!qab_bots)
+ return;
+
+ if (!IsAccount(np))
+ return;
+
+ for (b = qab_bots; b; b = b->next) {
+ if (b->micnumeric == np->numeric) {
+ b->micnumeric = 0;
+ sendmessagetochannel(b->np, b->staff_chan->channel, "Mic deactivated.");
+ if (!b->lastspam)
+ qabot_spamstored((void*)b);
+ }
+
+ if (b->recnumeric == np->numeric) {
+ b->recnumeric = 0;
+
+ if (b->recfile) {
+ fclose(b->recfile);
+ b->recfile = NULL;
+ }
+
+ sendmessagetochannel(b->np, b->staff_chan->channel, "Recorder deactivated.");
+ }
+ }
+}
+
+void qabot_channel_part(int hooknum, void* arg) {
+ channel* cp = (channel*)((void**)arg)[0];
+ nick* np = (nick*)((void**)arg)[1];
+ qab_bot* b;
+
+ if (!qab_bots)
+ return;
+
+ if (!IsAccount(np))
+ return;
+
+ for (b = qab_bots; b; b = b->next) {
+ if ((b->micnumeric == np->numeric) && (b->staff_chan->channel == cp)) {
+ b->micnumeric = 0;
+ sendmessagetochannel(b->np, b->staff_chan->channel, "Mic deactivated.");
+ if (!b->lastspam)
+ qabot_spamstored((void*)b);
+ }
+
+ if (b->recnumeric == np->numeric) {
+ b->recnumeric = 0;
+
+ if (b->recfile) {
+ fclose(b->recfile);
+ b->recfile = NULL;
+ }
+
+ sendmessagetochannel(b->np, b->staff_chan->channel, "Recorder deactivated.");
+ }
+ }
+}
+
+void qabot_createfakeuser(void* arg) {
+ channel* cp;
+
+ if ((qabot_nick = registerlocaluser(QABOT_NICK, QABOT_USER, QABOT_HOST,
+ QABOT_NAME, QABOT_ACCT, QABOT_UMDE, &qabot_handler))) {
+ if ((cp = findchannel(QABOT_HOMECHAN))) {
+ localjoinchannel(qabot_nick, cp);
+ localgetops(qabot_nick, cp);
+ }
+ else
+ localcreatechannel(qabot_nick, QABOT_HOMECHAN);
+ }
+}
+
+void qabot_handler(nick* me, int type, void** args) {
+ nick* sender;
+ Command* cmd;
+ channel* cp;
+ char* cargv[50];
+ int cargc;
+ qab_user* u;
+
+ switch (type) {
+ case LU_PRIVMSG:
+ case LU_SECUREMSG:
+ sender = (nick*)args[0];
+
+ if (!strncmp("\001VERSION", args[1], 8)) {
+ sendnoticetouser(qabot_nick, sender, "\001VERSION %s\001", QABOT_NAME);
+ return;
+ }
+
+ cargc = splitline((char*)args[1], cargv, 50, 0);
+ cmd = findcommandintree(qabot_commands, cargv[0], 1);
+ if (!cmd) {
+ sendnoticetouser(qabot_nick, sender, "Unknown command.");
+ return;
+ }
+
+ if (!IsAccount(sender)) {
+ sendnoticetouser(qabot_nick, sender, "You must be authed to use this command.");
+ return;
+ }
+
+ u = qabot_getuser(sender->authname);
+ if (cmd->level) {
+ if (!u) {
+ sendnoticetouser(qabot_nick, sender, "You need an account to use this command.");
+ return;
+ }
+
+ if ((cmd->level & QAUFLAG_STAFF) && !QAIsStaff(u)) {
+ sendnoticetouser(qabot_nick, sender, "You do not have access to this command.");
+ return;
+ }
+
+ if ((cmd->level & QAUFLAG_ADMIN) && !QAIsAdmin(u)) {
+ sendnoticetouser(qabot_nick, sender, "You do not have access to this command.");
+ return;
+ }
+
+ if ((cmd->level & QAUFLAG_DEVELOPER) && !QAIsDeveloper(u)) {
+ sendnoticetouser(qabot_nick, sender, "You do not have access to this command.");
+ return;
+ }
+ }
+
+ if (cmd->maxparams < (cargc-1)) {
+ rejoinline(cargv[cmd->maxparams], cargc - (cmd->maxparams));
+ cargc=(cmd->maxparams) + 1;
+ }
+
+ (cmd->handler)((void*)sender, cargc - 1, &(cargv[1]));
+
+ break;
+
+ case LU_KILLED:
+ qabot_nick = NULL;
+ scheduleoneshot(time(NULL) + 1, &qabot_createfakeuser, NULL);
+ break;
+
+ case LU_KICKED:
+ cp = (channel*)args[1];
+ if (cp) {
+ localjoinchannel(qabot_nick, cp);
+ localgetops(qabot_nick, cp);
+ }
+ break;
+ }
+}
+
+void qabot_child_handler(nick* me, int type, void** args) {
+ nick* sender;
+ channel* cp;
+ qab_bot* bot = me->exts[qabot_nickext];
+ char* text;
+ Command* cmd;
+ char* cargv[50];
+ int cargc;
+
+ if (!bot) {
+ return;
+ }
+
+ switch (type) {
+ case LU_CHANMSG:
+ sender = (nick*)args[0];
+ cp = (channel*)args[1];
+ text = (char*)args[2];
+
+ if (*text == '!') {
+ if (*(++text) == '\0') {
+ sendnoticetouser(me, sender, "No command specified.");
+ return;
+ }
+ cargc = splitline((char*)text, cargv, 50, 0);
+ cmd = findcommandintree(qabot_chancommands, cargv[0], 1);
+ if (!cmd) {
+ sendnoticetouser(me, sender, "Unknown command.");
+ return;
+ }
+
+ if ((cp->index == bot->staff_chan) && !(cmd->level & QAC_STAFFCHAN)) {
+ sendnoticetouser(me, sender, "This command cannot be used in the staff channel.");
+ return;
+ }
+
+ if ((cp->index == bot->question_chan) && !(cmd->level & QAC_QUESTIONCHAN)) {
+ sendnoticetouser(me, sender, "This command cannot be used in the question channel.");
+ return;
+ }
+
+ if (cmd->maxparams < (cargc-1)) {
+ rejoinline(cargv[cmd->maxparams], cargc - (cmd->maxparams));
+ cargc=(cmd->maxparams) + 1;
+ }
+
+ qabot_currentbot = bot;
+ qabot_currentchan = cp;
+
+ (cmd->handler)((void*)sender, cargc - 1, &(cargv[1]));
+ }
+ else if ((*text != '!') && (bot->micnumeric == sender->numeric) && (cp->index == bot->staff_chan)) {
+ bot->lastmic = time(NULL);
+ if (bot->lastspam) {
+ qab_spam* s;
+
+ s = (qab_spam*)malloc(sizeof(qab_spam));
+ s->message = strdup(text);
+ s->next = 0;
+ bot->lastspam->next = s;
+ bot->lastspam = s;
+ }
+ else {
+ if ((bot->spamtime + bot->spam_interval) < time(0)) {
+ sendmessagetochannel(me, bot->public_chan->channel, "%s", text);
+ bot->spammed++;
+ bot->spamtime = time(0);
+ }
+ else {
+ qab_spam* s;
+
+ s = (qab_spam*)malloc(sizeof(qab_spam));
+ s->message = strdup(text);
+ s->next = 0;
+ bot->nextspam = bot->lastspam = s;
+ scheduleoneshot(bot->spamtime + bot->spam_interval, qabot_spam, (void*)bot);
+ }
+ }
+ }
+ else if ((*text != '!') && (bot->recnumeric == sender->numeric) && (cp->index == bot->staff_chan)) {
+ if (bot->recfile) {
+ fprintf(bot->recfile, "%s\n", text);
+ }
+ }
+ break;
+
+ case LU_PRIVMSG:
+ case LU_SECUREMSG:
+ sender = (nick*)args[0];
+ text = (char*)args[1];
+ time_t lastmsg;
+
+ lastmsg = (time_t)sender->exts[qabot_spam_nickext];
+
+ if ((lastmsg + bot->ask_wait) > time(0)) {
+ sendnoticetouser(me, sender, "You have already sent a question recently, please wait at least %d seconds between asking questions.", bot->ask_wait);
+ return;
+ }
+ else {
+ char hostbuf[NICKLEN + USERLEN + HOSTLEN + 3];
+ qab_block* b;
+ qab_question* q;
+ unsigned long crc = 0;
+ int len;
+
+ sendnoticetouser(me, sender, "Your question has been relayed to the %s staff.", bot->public_chan->name->content);
+
+ if (!getnumerichandlefromchanhash(bot->public_chan->channel->users, sender->numeric))
+ return;
+
+ if (*text == '<')
+ text++;
+
+ len = strlen(text);
+ if ((len > 0) && (text[len - 1] == '>')) {
+ text[len - 1] = '\0';
+ len--;
+ }
+
+ if ((len < 5) || !strchr(text, ' ') || (*text == 1))
+ return;
+
+ if ((bot->flags & QAB_AUTHEDONLY) && !IsAccount(sender))
+ return;
+
+ if (bot->flags & QAB_CONTROLCHAR) {
+ char* ch;
+
+ for (ch = text; *ch; ch++)
+ if ((*ch == 2) || (*ch == 3) || (*ch == 22) || (*ch == 27) || (*ch == 31))
+ return;
+ }
+ else if (bot->flags & QAB_COLOUR) {
+ char* ch;
+
+ for (ch = text; *ch; ch++)
+ if (*ch == 3)
+ return;
+ }
+
+ if (bot->flags & QAB_FLOODDETECT) {
+ crc = crc32i(text);
+ if (crc == qab_lastq_crc) {
+ qab_lastq_count++;
+ if (qab_lastq_count >= 3) {
+ if ((qab_lastq_count == 3) || ((qab_lastq_count % 10) == 0))
+ sendmessagetochannel(me, bot->question_chan->channel, "WARNING: Possible question flood detected%s", (bot->flags & QAB_FLOODSTOP) ? " - AUTO IGNORED." : ".");
+ if (bot->flags & QAB_FLOODSTOP)
+ return;
+ }
+ }
+ else {
+ qab_lastq_crc = crc;
+ qab_lastq_count = 1;
+ }
+ }
+
+ sprintf(hostbuf,"%s!%s@%s", sender->nick, sender->ident, sender->host->name->content);
+
+ for (b = bot->blocks; b; b = b->next) {
+ switch (b->type) {
+ case QABBLOCK_ACCOUNT:
+ if (IsAccount(sender) && !ircd_strncmp(sender->authname, b->blockstr, ACCOUNTLEN))
+ return;
+ break;
+
+ case QABBLOCK_HOST:
+ if (!match(b->blockstr, hostbuf))
+ return;
+ break;
+
+ case QABBLOCK_TEXT:
+ if (!match(b->blockstr, text))
+ return;
+ break;
+ }
+ }
+
+ sender->exts[qabot_spam_nickext] = (void*)time(0);
+
+ q = (qab_question*)malloc(sizeof(qab_question));
+ q->id = ++bot->lastquestionID;
+ q->question = strdup(text);
+ q->flags = QAQ_NEW;
+ strncpy(q->nick, sender->nick, NICKLEN);
+ q->nick[NICKLEN] = '\0';
+ q->numeric = sender->numeric;
+ q->crc = (bot->flags & QAB_FLOODDETECT) ? crc : 0;
+ q->answer = 0;
+ q->next = bot->questions[q->id % QUESTIONHASHSIZE];
+
+ bot->questions[q->id % QUESTIONHASHSIZE] = q;
+
+ sendmessagetochannel(me, bot->question_chan->channel, "ID: %3d <%s> %s", q->id, visiblehostmask(sender, hostbuf), text);
+
+ if ((bot->flags & QAB_LINEBREAK) && ((bot->lastquestionID % 10) == 0))
+ sendmessagetochannel(me, bot->question_chan->channel, "-----------------------------------");
+ }
+ break;
+
+ case LU_KILLED:
+ bot->np = NULL;
+ scheduleoneshot(time(NULL) + 1, &qabot_createbotfakeuser, (void*)bot);
+ break;
+
+ case LU_KICKED:
+ cp = (channel*)args[1];
+ if (cp) {
+ localjoinchannel(bot->np, cp);
+ localgetops(bot->np, cp);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+char* qabot_getvictim(nick* np, char* target) {
+ if (*target != '#') {
+ nick* victim;
+
+ if (!(victim = getnickbynick(target))) {
+ sendnoticetouser(qabot_nick, np, "No such nickname.");
+ return 0;
+ }
+
+ if (!IsAccount(victim)) {
+ sendnoticetouser(qabot_nick, np, "%s is not authed.", victim->nick);
+ return 0;
+ }
+
+ return victim->authname;
+ }
+
+ return target + 1;
+}
+
+const char* qabot_uflagtostr(flag_t flags) {
+ static char buf[20];
+ int i = 0;
+
+ buf[i++] = '+';
+
+ if (flags & QAUFLAG_ADMIN) { buf[i++] = 'a'; }
+ if (flags & QAUFLAG_DEVELOPER) { buf[i++] = 'd'; }
+ if (flags & QAUFLAG_STAFF) { buf[i++] = 's'; }
+
+ buf[i] = '\0';
+
+ return buf;
+}
+
+const char* qabot_formattime(time_t tme) {
+ static char buf[50];
+ struct tm* tmp;
+
+ tmp = gmtime(&tme);
+ strftime(buf, 15, "%d/%m/%y %H:%M", tmp);
+
+ return buf;
+}
+
+qab_bot* qabot_getcurrentbot() {
+ return qabot_currentbot;
+}
+
+channel* qabot_getcurrentchannel() {
+ return qabot_currentchan;
+}