X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=src%2Fmod-helpserv.c;h=bd1cf1c3ffde831e7cc1993e3efe6ad635adf984;hb=6ad7e65ad5121cd1ad3f2f19c7e9a9912cfa44f0;hp=5e8a5139a5206415af674308475081973bbde587;hpb=565b871f7cd94c40eca88a05d60bd6d9acae0be5;p=srvx.git diff --git a/src/mod-helpserv.c b/src/mod-helpserv.c index 5e8a513..bd1cf1c 100644 --- a/src/mod-helpserv.c +++ b/src/mod-helpserv.c @@ -1,5 +1,5 @@ /* mod-helpserv.c - Support Helper assistant service - * Copyright 2002-2003 srvx Development Team + * Copyright 2002-2003, 2006 srvx Development Team * * This file is part of srvx. * @@ -53,6 +53,7 @@ const char *helpserv_module_deps[] = { NULL }; #define KEY_NICK "nick" #define KEY_DB_BADCHANS "badchans" #define KEY_HELP_CHANNEL "help_channel" +#define KEY_PUBLIC_CHANNEL "public_channel" #define KEY_PAGE_DEST "page_dest" #define KEY_CMDWORD "cmdword" #define KEY_PERSIST_LENGTH "persist_length" @@ -85,13 +86,14 @@ const char *helpserv_module_deps[] = { NULL }; #define KEY_PRIVMSG_ONLY "privmsg_only" #define KEY_REQ_ON_JOIN "req_on_join" #define KEY_AUTO_VOICE "auto_voice" +#define KEY_AUTO_JOIN "auto_join" #define KEY_AUTO_DEVOICE "auto_devoice" #define KEY_LAST_ACTIVE "last_active" /* General */ #define HSFMT_TIME "%a, %d %b %Y %H:%M:%S %Z" static const struct message_entry msgtab[] = { - { "HSMSG_READHELP_SUCCESS", "Read HelpServ help database in "FMT_TIME_T".%03ld seconds." }, + { "HSMSG_READHELP_SUCCESS", "Read HelpServ help database in %lu.%03lu seconds." }, { "HSMSG_INVALID_BOT", "This command requires a valid HelpServ bot name." }, { "HSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel; cannot use it." }, { "HSMSG_INTERNAL_COMMAND", "$b%s$b appears to be an internal HelpServ command, sorry." }, @@ -123,6 +125,10 @@ static const struct message_entry msgtab[] = { { "HSMSG_EXPIRATION_DONE", "%d eligible HelpServ bots have retired." }, { "HSMSG_BAD_WEEKDAY", "I do not know which day of the week $b%s$b is." }, { "HSMSG_WEEK_STARTS", "$b%s$b's weeks start on $b%s$b." }, + { "HSMSG_MODSTATS_BAD_FIELD", "The specified field does not exist." }, + { "HSMSG_MODSTATS_BAD_WEEK", "The specified week is invalid." }, + { "HSMSG_MODSTATS_NEGATIVE", "This modification would result in a negative value." }, + { "HSMSG_MODSTATS_SUCCESS", "$b%s$b's stats have been modified successfully." }, /* Registration */ { "HSMSG_ILLEGAL_NICK", "$b%s$b is an illegal nick; cannot use it." }, @@ -166,7 +172,9 @@ static const struct message_entry msgtab[] = { { "HSMSG_SET_PRIVMSGONLY", "$bPrivmsgOnly $b %s" }, { "HSMSG_SET_REQONJOIN", "$bReqOnJoin $b %s" }, { "HSMSG_SET_AUTOVOICE", "$bAutoVoice $b %s" }, + { "HSMSG_SET_AUTOJOIN", "$bAutoJoin $b %s" }, { "HSMSG_SET_AUTODEVOICE", "$bAutoDevoice $b %s" }, + { "HSMSG_SET_PUBLICCHAN", "$bPublicChan $b %s" }, { "HSMSG_PAGE_NOTICE", "notice" }, { "HSMSG_PAGE_PRIVMSG", "privmsg" }, { "HSMSG_PAGE_ONOTICE", "onotice" }, @@ -218,6 +226,7 @@ static const struct message_entry msgtab[] = { { "HSMSG_REQ_PERSIST_PART", "Everything you tell me until you are helped (or you leave %s) will be recorded. If you part %s, your request will be lost." }, { "HSMSG_REQ_PERSIST_HANDLE", "Everything you tell me until you are helped will be recorded." }, { "HSMSG_REQ_MAXLEN", "Sorry, but your request has reached the maximum number of lines. Please wait to be assigned to a helper and continue explaining your request to them." }, + { "HSMSQ_REQ_TEXT_ADDED", "Message from $b%s:$b %s" }, { "HSMSG_REQ_FOUND_ANOTHER", "Request ID#%lu has been closed. $S detected that you also have request ID#%lu open. If you send $S a message, it will be associated with that request." }, /* Messages that are inserted into request text */ @@ -241,7 +250,7 @@ static const struct message_entry msgtab[] = { { "HSMSG_PAGE_HELPER_GONE_2", "Request ID#%lu from $b%s$b (Not authed) $bhas been unassigned$b, as its helper, %s has %s." }, { "HSMSG_PAGE_HELPER_GONE_3", "Request ID#%lu from an offline user (Account %s) $bhas been unassigned$b, as its helper, %s has %s." }, { "HSMSG_PAGE_HELPER_GONE_4", "Request ID#%lu from an offline user (No account) $bhas been unassigned$b, as its helper, %s has %s." }, - { "HSMSG_PAGE_WHINE_HEADER", "$b%u unhandled request(s)$b waiting at least $b%s$b (%u total)" }, + { "HSMSG_PAGE_WHINE_HEADER", "$b%u unhandled request(s)$b waiting at least $b%s$b (%u unhandled, %u total)" }, { "HSMSG_PAGE_IDLE_HEADER", "$b%u users$b in %s $bidle at least %s$b:" }, { "HSMSG_PAGE_EMPTYALERT", "$b%s has no helpers present (%u unhandled request(s))$b" }, { "HSMSG_PAGE_ONLYTRIALALERT", "$b%s has no full helpers present (%d trial(s) present; %u unhandled request(s))$b" }, @@ -476,10 +485,11 @@ static struct { const char *reqlogfile; unsigned long db_backup_frequency; unsigned int expire_age; + unsigned long modstats_level; char user_escape; } helpserv_conf; -static time_t last_stats_update; +static unsigned long last_stats_update; static int shutting_down; static FILE *reqlog_f; static struct log_type *HS_LOG; @@ -494,6 +504,7 @@ struct helpserv_bot { struct userNode *helpserv; struct chanNode *helpchan; + struct chanNode *publicchan; struct chanNode *page_targets[PGSRC_COUNT]; enum page_type page_types[PGSRC_COUNT]; @@ -502,7 +513,7 @@ struct helpserv_bot { enum notification_type notify; /* This is a default; it can be changed on a per-request basis */ - enum persistence_type persist_types[PERSIST_T_COUNT]; + enum persistence_length persist_lengths[PERSIST_T_COUNT]; dict_t users; /* indexed by handle */ @@ -515,12 +526,13 @@ struct helpserv_bot { unsigned int privmsg_only : 1; unsigned int req_on_join : 1; unsigned int auto_voice : 1; + unsigned int auto_join : 1; unsigned int auto_devoice : 1; unsigned int helpchan_empty : 1; - time_t registered; - time_t last_active; + unsigned long registered; + unsigned long last_active; char *registrar; }; @@ -531,7 +543,7 @@ struct helpserv_user { unsigned int week_start : 3; enum helpserv_level level; /* statistics */ - time_t join_time; /* when they joined, or 0 if not in channel */ + unsigned long join_time; /* when they joined, or 0 if not in channel */ /* [0] through [3] are n weeks ago, [4] is the total of everything before that */ unsigned int time_per_week[5]; /* how long they've were in the channel the past 4 weeks */ unsigned int picked_up[5]; /* how many requests they have picked up */ @@ -562,9 +574,9 @@ struct helpserv_request { struct handle_info *handle; unsigned long id; - time_t opened; - time_t assigned; - time_t updated; + unsigned long opened; + unsigned long assigned; + unsigned long updated; }; #define DEFINE_LIST_ALLOC(STRUCTNAME) \ @@ -581,16 +593,16 @@ void STRUCTNAME##_free(void *data) {\ } DECLARE_LIST(helpserv_botlist, struct helpserv_bot *); -DEFINE_LIST(helpserv_botlist, struct helpserv_bot *); -DEFINE_LIST_ALLOC(helpserv_botlist); +DEFINE_LIST(helpserv_botlist, struct helpserv_bot *) +DEFINE_LIST_ALLOC(helpserv_botlist) DECLARE_LIST(helpserv_reqlist, struct helpserv_request *); -DEFINE_LIST(helpserv_reqlist, struct helpserv_request *); -DEFINE_LIST_ALLOC(helpserv_reqlist); +DEFINE_LIST(helpserv_reqlist, struct helpserv_request *) +DEFINE_LIST_ALLOC(helpserv_reqlist) DECLARE_LIST(helpserv_userlist, struct helpserv_user *); -DEFINE_LIST(helpserv_userlist, struct helpserv_user *); -DEFINE_LIST_ALLOC(helpserv_userlist); +DEFINE_LIST(helpserv_userlist, struct helpserv_user *) +DEFINE_LIST_ALLOC(helpserv_userlist) struct helpfile *helpserv_helpfile; static struct module *helpserv_module; @@ -623,31 +635,43 @@ static HELPSERV_FUNC(cmd_help); return 0; } /* For messages going to users being helped */ -#define helpserv_msguser(target, format...) send_message_type((from_opserv ? 0 : hs->privmsg_only), (target), (from_opserv ? opserv : hs->helpserv) , ## format) -#define helpserv_user_reply(format...) send_message_type(req->hs->privmsg_only, req->user, req->hs->helpserv , ## format) +#if defined(GCC_VARMACROS) +# define helpserv_msguser(target, ARGS...) send_message_type((from_opserv ? 0 : hs->privmsg_only), (target), (from_opserv ? opserv : hs->helpserv), ARGS) +# define helpserv_user_reply(ARGS...) send_message_type(req->hs->privmsg_only, req->user, req->hs->helpserv, ARGS) /* For messages going to helpers */ -#define helpserv_notice(target, format...) send_message((target), (from_opserv ? opserv : hs->helpserv) , ## format) -#define helpserv_notify(helper, format...) do { struct userNode *_target; for (_target = (helper)->handle->users; _target; _target = _target->next_authed) { \ - send_message(_target, (helper)->hs->helpserv, ## format); \ +# define helpserv_notice(target, ARGS...) send_message((target), (from_opserv ? opserv : hs->helpserv), ARGS) +# define helpserv_notify(helper, ARGS...) do { struct userNode *_target; for (_target = (helper)->handle->users; _target; _target = _target->next_authed) { \ + if(!_target->next_authed || GetUserMode(helper->hs->helpchan, _target)) {\ + send_message(_target, (helper)->hs->helpserv, ARGS); \ + break; \ + } \ + } } while (0) +# define helpserv_page(TYPE, ARGS...) do { \ + int msg_type=0; struct chanNode *target=helpserv_get_page_type(hs, (TYPE), &msg_type); \ + if (target) send_target_message(msg_type, target->name, hs->helpserv, ARGS); \ + } while (0) +#elif defined(C99_VARMACROS) +# define helpserv_msguser(target, ...) send_message_type((from_opserv ? 0 : hs->privmsg_only), (target), (from_opserv ? opserv : hs->helpserv), __VA_ARGS__) +# define helpserv_user_reply(...) send_message_type(req->hs->privmsg_only, req->user, req->hs->helpserv, __VA_ARGS__) +/* For messages going to helpers */ +# define helpserv_notice(target, ...) send_message((target), (from_opserv ? opserv : hs->helpserv), __VA_ARGS__) +# define helpserv_notify(helper, ...) do { struct userNode *_target; for (_target = (helper)->handle->users; _target; _target = _target->next_authed) { \ + if(!_target->next_authed || GetUserMode(helper->hs->helpchan, _target)) {\ + send_message(_target, (helper)->hs->helpserv, __VA_ARGS__); \ + break; \ + } \ } } while (0) +# define helpserv_page(TYPE, ...) do { \ + int msg_type=0; struct chanNode *target=helpserv_get_page_type(hs, (TYPE), &msg_type); \ + if (target) send_target_message(msg_type, target->name, hs->helpserv, __VA_ARGS__); \ + } while (0) +#endif #define helpserv_message(hs, target, id) do { if ((hs)->messages[id]) { \ if (from_opserv) \ send_message_type(4, (target), opserv, "%s", (hs)->messages[id]); \ else \ send_message_type(4 | hs->privmsg_only, (target), hs->helpserv, "%s", (hs)->messages[id]); \ } } while (0) -#define helpserv_page(TYPE, FORMAT...) do { \ - struct chanNode *target=NULL; int msg_type=0; \ - target = hs->page_targets[TYPE]; \ - switch (hs->page_types[TYPE]) { \ - case PAGE_NOTICE: msg_type = 0; break; \ - case PAGE_PRIVMSG: msg_type = 1; break; \ - case PAGE_ONOTICE: msg_type = 2; break; \ - default: log_module(HS_LOG, LOG_ERROR, "helpserv_page() called but %s has an invalid page type %d.", hs->helpserv->nick, TYPE); \ - case PAGE_NONE: target = NULL; break; \ - } \ - if (target) send_target_message(msg_type, target->name, hs->helpserv, ## FORMAT); \ - } while (0) #define helpserv_get_handle_info(user, text) smart_get_handle_info((from_opserv ? opserv : hs->helpserv) , (user), (text)) struct helpserv_cmd { @@ -659,7 +683,7 @@ struct helpserv_cmd { static void run_empty_interval(void *data); -static void helpserv_interval(char *output, time_t interval) { +static void helpserv_interval(char *output, unsigned long interval) { int num_hours = interval / 3600; int num_minutes = (interval % 3600) / 60; sprintf(output, "%u hour%s, %u minute%s", num_hours, num_hours == 1 ? "" : "s", num_minutes, num_minutes == 1 ? "" : "s"); @@ -696,10 +720,10 @@ static void helpserv_log_request(struct helpserv_request *req, const char *reaso assert(req != NULL); assert(reason != NULL); - if (!(ctx = saxdb_open_context(reqlog_f))) + if (!reqlog_f || !(ctx = saxdb_open_context(reqlog_f))) return; - sprintf(key, "%s-" FMT_TIME_T "-%lu", req->hs->helpserv->nick, req->opened, req->id); - if ((res = setjmp(ctx->jbuf)) != 0) { + sprintf(key, "%s-%lu-%lu", req->hs->helpserv->nick, (unsigned long)req->opened, req->id); + if ((res = setjmp(*saxdb_jmp_buf(ctx))) != 0) { log_module(HS_LOG, LOG_ERROR, "Unable to log helpserv request: %s.", strerror(res)); } else { saxdb_start_record(ctx, key, 1); @@ -720,11 +744,31 @@ static void helpserv_log_request(struct helpserv_request *req, const char *reaso saxdb_write_string(ctx, KEY_REQUEST_CLOSEREASON, reason); saxdb_write_string_list(ctx, KEY_REQUEST_TEXT, req->text); saxdb_end_record(ctx); - saxdb_close_context(ctx); - fflush(reqlog_f); + saxdb_close_context(ctx, 0); } } +static struct chanNode *helpserv_get_page_type(struct helpserv_bot *hs, enum page_source type, int *msg_type) +{ + switch (hs->page_types[type]) { + case PAGE_NOTICE: + *msg_type = 0; + break; + case PAGE_PRIVMSG: + *msg_type = 1; + break; + case PAGE_ONOTICE: + *msg_type = 2; + break; + default: + log_module(HS_LOG, LOG_ERROR, "helpserv_page() called but %s has an invalid page type %d.", hs->helpserv->nick, type); + /* and fall through */ + case PAGE_NONE: + return NULL; + } + return hs->page_targets[type]; +} + /* Searches for a request by number, nick, or account (num|nick|*account). * As there can potentially be >1 match, it takes a reqlist. The return * value is the "best" request found (explained in the comment block below). @@ -821,7 +865,7 @@ static struct helpserv_request * smart_get_request(struct helpserv_bot *hs, stru static struct helpserv_request * create_request(struct userNode *user, struct helpserv_bot *hs, int from_join) { struct helpserv_request *req = calloc(1, sizeof(struct helpserv_request)); - char lbuf[3][MAX_LINE_SIZE], unh[INTERVALLEN]; + char lbuf[3][MAX_LINE_SIZE], req_id[INTERVALLEN]; struct helpserv_reqlist *reqlist, *hand_reqlist; const unsigned int from_opserv = 0; const char *fmt; @@ -829,8 +873,8 @@ static struct helpserv_request * create_request(struct userNode *user, struct he assert(req); req->id = ++hs->last_requestid; - sprintf(unh, "%lu", req->id); - dict_insert(hs->requests, strdup(unh), req); + sprintf(req_id, "%lu", req->id); + dict_insert(hs->requests, strdup(req_id), req); if (hs->id_wrap) { unsigned long i; @@ -864,7 +908,7 @@ static struct helpserv_request * create_request(struct userNode *user, struct he req->user = user; req->handle = user->handle_info; if (from_join && self->burst) { - extern time_t burst_begin; + extern unsigned long burst_begin; /* We need to keep all the requests during a burst join together, * even if the burst takes more than 1 second. ircu seems to burst * in reverse-join order. */ @@ -924,32 +968,31 @@ static struct helpserv_request * create_request(struct userNode *user, struct he sprintf(lbuf[0], fmt, req->id); } if (req != hs->unhandled) { - intervalString(unh, now - hs->unhandled->opened, user->handle_info); + intervalString(req_id, now - hs->unhandled->opened, user->handle_info); fmt = user_find_message(user, "HSMSG_REQ_UNHANDLED_TIME"); - sprintf(lbuf[1], fmt, unh); + sprintf(lbuf[1], fmt, req_id); } else { fmt = user_find_message(user, "HSMSG_REQ_NO_UNHANDLED"); - sprintf(lbuf[1], fmt); + sprintf(lbuf[1], "%s", fmt); } - switch (hs->persist_types[PERSIST_T_REQUEST]) { + switch (hs->persist_lengths[PERSIST_T_REQUEST]) { case PERSIST_PART: fmt = user_find_message(user, "HSMSG_REQ_PERSIST_PART"); sprintf(lbuf[2], fmt, hs->helpchan->name, hs->helpchan->name); break; case PERSIST_QUIT: fmt = user_find_message(user, "HSMSG_REQ_PERSIST_QUIT"); - sprintf(lbuf[2], fmt); + sprintf(lbuf[2], "%s", fmt); break; default: log_module(HS_LOG, LOG_ERROR, "%s has an invalid req_persist.", hs->helpserv->nick); case PERSIST_CLOSE: if (user->handle_info) { fmt = user_find_message(user, "HSMSG_REQ_PERSIST_HANDLE"); - sprintf(lbuf[2], fmt); } else { fmt = user_find_message(user, "HSMSG_REQ_PERSIST_QUIT"); - sprintf(lbuf[2], fmt); } + sprintf(lbuf[2], "%s", fmt); break; } helpserv_message(hs, user, MSGTYPE_REQ_OPENED); @@ -967,7 +1010,7 @@ static struct helpserv_request * create_request(struct userNode *user, struct he } /* Handle a message from a user to a HelpServ bot. */ -static void helpserv_usermsg(struct userNode *user, struct helpserv_bot *hs, char *text) { +static void helpserv_usermsg(struct userNode *user, struct helpserv_bot *hs, const char *text) { const int from_opserv = 0; /* for helpserv_notice */ struct helpserv_request *req=NULL, *newest=NULL; struct helpserv_reqlist *reqlist, *hand_reqlist; @@ -1023,8 +1066,11 @@ static void helpserv_usermsg(struct userNode *user, struct helpserv_bot *hs, cha helpserv_msguser(user, "HSMSG_USERCMD_NO_REQUEST"); return; } - if ((hs->persist_types[PERSIST_T_REQUEST] == PERSIST_PART) && !GetUserMode(hs->helpchan, user)) { - helpserv_msguser(user, "HSMSG_REQ_YOU_NOT_IN_HELPCHAN_OPEN", hs->helpchan->name); + if ((hs->persist_lengths[PERSIST_T_REQUEST] == PERSIST_PART) && !GetUserMode(hs->helpchan, user) && (!hs->publicchan || (hs->publicchan && !GetUserMode(hs->publicchan, user)))) { + if(hs->publicchan) + helpserv_msguser(user, "HSMSG_REQ_YOU_NOT_IN_HELPCHAN_OPEN", hs->publicchan->name); + else + helpserv_msguser(user, "HSMSG_REQ_YOU_NOT_IN_HELPCHAN_OPEN", hs->helpchan->name); return; } @@ -1062,40 +1108,58 @@ static void helpserv_usermsg(struct userNode *user, struct helpserv_bot *hs, cha helpserv_msguser(user, "HSMSG_USERCMD_UNKNOWN", cmdname); return; } else if (hs->intervals[INTERVAL_STALE_DELAY] - && (req->updated < (time_t)(now - hs->intervals[INTERVAL_STALE_DELAY])) + && (req->updated < now - hs->intervals[INTERVAL_STALE_DELAY]) && (!hs->req_maxlen || req->text->used < hs->req_maxlen)) { char buf[MAX_LINE_SIZE], updatestr[INTERVALLEN], timestr[MAX_LINE_SIZE]; + time_t feh; - strftime(timestr, MAX_LINE_SIZE, HSFMT_TIME, localtime(&req->opened)); + feh = req->opened; + strftime(timestr, MAX_LINE_SIZE, HSFMT_TIME, localtime(&feh)); intervalString(updatestr, now - req->updated, user->handle_info); if (req->helper && (hs->notify >= NOTIFY_USER)) if (user->handle_info) helpserv_notify(req->helper, "HSMSG_PAGE_UPD_REQUEST_AUTHED", req->id, user->nick, user->handle_info->handle, timestr, updatestr); else - helpserv_notify(req->helper, "HSMSG_PAGE_UPD_REQUESTNOT_AUTHED", req->id, user->nick, timestr, updatestr); + helpserv_notify(req->helper, "HSMSG_PAGE_UPD_REQUEST_NOT_AUTHED", req->id, user->nick, timestr, updatestr); else if (user->handle_info) helpserv_page(PGSRC_STATUS, "HSMSG_PAGE_UPD_REQUEST_AUTHED", req->id, user->nick, user->handle_info->handle, timestr, updatestr); else helpserv_page(PGSRC_STATUS, "HSMSG_PAGE_UPD_REQUEST_NOT_AUTHED", req->id, user->nick, timestr, updatestr); - strftime(timestr, MAX_LINE_SIZE, HSFMT_TIME, localtime(&now)); + feh = now; + strftime(timestr, MAX_LINE_SIZE, HSFMT_TIME, localtime(&feh)); snprintf(buf, MAX_LINE_SIZE, "[Stale request updated at %s]", timestr); string_list_append(req->text, strdup(buf)); } req->updated = now; - if (!hs->req_maxlen || req->text->used < hs->req_maxlen) + if (!hs->req_maxlen || req->text->used < hs->req_maxlen) { + struct userNode *likely_helper; + string_list_append(req->text, strdup(text)); - else + /* Find somebody likely to be the helper */ + if (!req->helper) + likely_helper = NULL; + else if ((likely_helper = req->helper->handle->users) && !likely_helper->next_authed) { + /* only one user it could be :> */ + } else for (likely_helper = req->helper->handle->users; likely_helper; likely_helper = likely_helper->next_authed) + if (GetUserMode(hs->helpchan, likely_helper)) + break; + + if(likely_helper) + send_target_message(1, likely_helper->nick, hs->helpserv, "HSMSQ_REQ_TEXT_ADDED", user->nick, text); + + } else helpserv_msguser(user, "HSMSG_REQ_MAXLEN"); } /* Handle messages direct to a HelpServ bot. */ -static void helpserv_botmsg(struct userNode *user, struct userNode *target, char *text, UNUSED_ARG(int server_qualified)) { +static void helpserv_botmsg(struct userNode *user, struct userNode *target, const char *text, UNUSED_ARG(int server_qualified)) { struct helpserv_bot *hs; struct helpserv_cmd *cmd; struct helpserv_user *hs_user; char *argv[MAXNUMPARAMS]; + char tmpline[MAXLEN]; int argc, argv_shift; const int from_opserv = 0; /* for helpserv_notice */ @@ -1113,7 +1177,8 @@ static void helpserv_botmsg(struct userNode *user, struct userNode *target, char } argv_shift = 1; - argc = split_line(text, false, ArrayLength(argv)-argv_shift, argv+argv_shift); + safestrncpy(tmpline, text, sizeof(tmpline)); + argc = split_line(tmpline, false, ArrayLength(argv)-argv_shift, argv+argv_shift); if (!argc) return; @@ -1133,8 +1198,8 @@ static void helpserv_botmsg(struct userNode *user, struct userNode *target, char if (!cmd->func) { helpserv_notice(user, "HSMSG_INTERNAL_COMMAND", argv[argv_shift]); } else if (cmd->func(user, hs, 0, argc, argv+argv_shift)) { - unsplit_string(argv+argv_shift, argc, text); - log_audit(HS_LOG, LOG_COMMAND, user, hs->helpserv, hs->helpchan->name, 0, text); + unsplit_string(argv+argv_shift, argc, tmpline); + log_audit(HS_LOG, LOG_COMMAND, user, hs->helpserv, hs->helpchan->name, 0, tmpline); } } @@ -1234,7 +1299,7 @@ static HELPSERV_USERCMD(usercmd_wait) { return; } - for (other = req->hs->unhandled, pos = -1, count = 0; + for (other = req->hs->unhandled, pos = -1, count = 0; other; other = other->next_unhandled, ++count) { if (other == req) @@ -1275,7 +1340,7 @@ static HELPSERV_FUNC(cmd_readhelp) { stop.tv_sec -= 1; stop.tv_usec += 1000000; } - helpserv_notice(user, "HSMSG_READHELP_SUCCESS", stop.tv_sec, stop.tv_usec/1000); + helpserv_notice(user, "HSMSG_READHELP_SUCCESS", (unsigned long)stop.tv_sec, (unsigned long)stop.tv_usec/1000); return 1; } @@ -1306,7 +1371,7 @@ static void helpserv_del_user(struct helpserv_bot *hs, struct helpserv_user *hs_ } static int cmd_add_user(struct helpserv_bot *hs, int from_opserv, struct userNode *user, enum helpserv_level level, int argc, char *argv[]) { - struct helpserv_user *actor, *new_user; + struct helpserv_user *actor; struct handle_info *handle; REQUIRE_PARMS(2); @@ -1334,7 +1399,7 @@ static int cmd_add_user(struct helpserv_bot *hs, int from_opserv, struct userNod return 0; } - new_user = helpserv_add_user(hs, handle, level); + helpserv_add_user(hs, handle, level); helpserv_notice(user, "HSMSG_ADDED_USER", helpserv_level2str(level), handle->handle); return 1; @@ -1549,7 +1614,7 @@ static void free_request(void *data) { struct helpserv_request *req = data; /* Logging */ - if (shutting_down && (req->hs->persist_types[PERSIST_T_REQUEST] != PERSIST_CLOSE || !req->handle)) { + if (shutting_down && (req->hs->persist_lengths[PERSIST_T_REQUEST] != PERSIST_CLOSE || !req->handle)) { helpserv_log_request(req, "srvx shutdown"); } @@ -1788,6 +1853,7 @@ static void helpserv_show(int from_opserv, struct helpserv_bot *hs, struct userN unsigned int nn; char buf[MAX_LINE_SIZE]; char buf2[INTERVALLEN]; + time_t feh; if (req->user) if (req->handle) @@ -1801,7 +1867,8 @@ static void helpserv_show(int from_opserv, struct helpserv_bot *hs, struct userN helpserv_notice(user, "HSMSG_REQ_INFO_2d", req->handle->handle); else helpserv_notice(user, "HSMSG_REQ_INFO_2e"); - strftime(buf, MAX_LINE_SIZE, HSFMT_TIME, localtime(&req->opened)); + feh = req->opened; + strftime(buf, MAX_LINE_SIZE, HSFMT_TIME, localtime(&feh)); intervalString(buf2, now - req->opened, user->handle_info); helpserv_notice(user, "HSMSG_REQ_INFO_3", buf, buf2); helpserv_notice(user, "HSMSG_REQ_INFO_4"); @@ -1817,7 +1884,7 @@ static int helpserv_assign(int from_opserv, struct helpserv_bot *hs, struct user if (!user->handle_info) return 0; - if ((hs->persist_types[PERSIST_T_HELPER] == PERSIST_PART) && !GetUserMode(hs->helpchan, user)) { + if ((hs->persist_lengths[PERSIST_T_HELPER] == PERSIST_PART) && !GetUserMode(hs->helpchan, user)) { struct helpserv_user *hsuser_actor = GetHSUser(hs, actor->handle_info); if (hsuser_actor->level < HlManager) { helpserv_notice(user, "HSMSG_REQ_YOU_NOT_IN_HELPCHAN", hs->helpchan->name); @@ -1844,7 +1911,11 @@ static int helpserv_assign(int from_opserv, struct helpserv_bot *hs, struct user req->helper = GetHSUser(hs, user->handle_info); assert(req->helper); req->assigned = now; - + + if (req->user && hs->auto_join) { + irc_svsjoin(hs->helpserv,req->user,hs->helpchan); + } + if (old_helper) { helpserv_notice(user, "HSMSG_REQ_REASSIGNED", req->id, old_helper->handle->handle); req->helper->reassigned_to[0]++; @@ -1883,7 +1954,7 @@ static int helpserv_assign(int from_opserv, struct helpserv_bot *hs, struct user if ((change.args[0].u.member = GetUserMode(hs->helpchan, req->user))) mod_chanmode_announce(hs->helpserv, hs->helpchan, &change); } - + return 1; } @@ -1971,7 +2042,7 @@ static HELPSERV_FUNC(cmd_reassign) { return 0; } - if ((hs->persist_types[PERSIST_T_HELPER] == PERSIST_PART) && !GetUserMode(hs->helpchan, user) && (hs_user->level < HlManager)) { + if ((hs->persist_lengths[PERSIST_T_HELPER] == PERSIST_PART) && !GetUserMode(hs->helpchan, user) && (hs_user->level < HlManager)) { helpserv_notice(user, "HSMSG_REQ_HIM_NOT_IN_HELPCHAN", targetuser->nick, hs->helpchan->name); return 0; } @@ -1985,6 +2056,7 @@ static HELPSERV_FUNC(cmd_addnote) { struct helpserv_request *req; struct helpserv_user *hs_user=GetHSUser(hs, user->handle_info); int num_requests=0; + time_t feh; REQUIRE_PARMS(3); @@ -1998,7 +2070,8 @@ static HELPSERV_FUNC(cmd_addnote) { note = unsplit_string(argv+2, argc-2, NULL); - strftime(timestr, MAX_LINE_SIZE, HSFMT_TIME, localtime(&now)); + feh = now; + strftime(timestr, MAX_LINE_SIZE, HSFMT_TIME, localtime(&feh)); snprintf(text, MAX_LINE_SIZE, "[Helper note at %s]:", timestr); string_list_append(req->text, strdup(text)); snprintf(text, MAX_LINE_SIZE, " <%s> %s", user->handle_info->handle, note); @@ -2296,7 +2369,7 @@ static HELPSERV_FUNC(cmd_bots) { struct helpserv_user *owner=NULL; bot = iter_data(it); - + for (it2=dict_first(bot->users); it2; it2=iter_next(it2)) { if (((struct helpserv_user *)iter_data(it2))->level == HlOwner) { owner = iter_data(it2); @@ -2387,7 +2460,7 @@ static void run_whine_interval(void *data) { for (unh = hs->unhandled; unh; unh = unh->next_unhandled) { queuesize++; - if ((now - unh->opened) >= (time_t)hs->intervals[INTERVAL_WHINE_DELAY]) { + if ((now - unh->opened) >= hs->intervals[INTERVAL_WHINE_DELAY]) { helpserv_reqlist_append(&reqlist, unh); } } @@ -2395,7 +2468,7 @@ static void run_whine_interval(void *data) { if (reqlist.used) { char strwhinedelay[INTERVALLEN]; - intervalString(strwhinedelay, (time_t)hs->intervals[INTERVAL_WHINE_DELAY], NULL); + intervalString(strwhinedelay, hs->intervals[INTERVAL_WHINE_DELAY], NULL); #if ANNOYING_ALERT_PAGES tbl.length = reqlist.used + 1; tbl.width = 4; @@ -2420,7 +2493,7 @@ static void run_whine_interval(void *data) { tbl.contents[i][3] = strdup(unh_time); } - helpserv_page(PGSRC_ALERT, "HSMSG_PAGE_WHINE_HEADER", reqlist.used, strwhinedelay, queuesize); + helpserv_page(PGSRC_ALERT, "HSMSG_PAGE_WHINE_HEADER", reqlist.used, strwhinedelay, queuesize, dict_size(hs->requests)); table_send(hs->helpserv, hs->page_targets[PGSRC_ALERT]->name, 0, page_type_funcs[hs->page_types[PGSRC_ALERT]], tbl); for (i=1; i <= reqlist.used; i++) { @@ -2428,7 +2501,7 @@ static void run_whine_interval(void *data) { free((char *)tbl.contents[i][3]); } #else - helpserv_page(PGSRC_ALERT, "HSMSG_PAGE_WHINE_HEADER", reqlist.used, strwhinedelay, queuesize); + helpserv_page(PGSRC_ALERT, "HSMSG_PAGE_WHINE_HEADER", reqlist.used, strwhinedelay, queuesize, dict_size(hs->requests)); #endif } @@ -2500,7 +2573,7 @@ static void run_whine_interval(void *data) { tbl.contents[i][3] = strdup(idle_time); } - intervalString(stridledelay, (time_t)hs->intervals[INTERVAL_IDLE_DELAY], NULL); + intervalString(stridledelay, hs->intervals[INTERVAL_IDLE_DELAY], NULL); helpserv_page(PGSRC_STATUS, "HSMSG_PAGE_IDLE_HEADER", mode_list.used, hs->helpchan->name, stridledelay); table_send(hs->helpserv, hs->page_targets[PGSRC_STATUS]->name, 0, page_types[hs->page_types[PGSRC_STATUS]].func, tbl); @@ -2599,7 +2672,7 @@ static struct helpserv_bot *register_helpserv(const char *nick, const char *help * it's a harmless default */ hs = calloc(1, sizeof(struct helpserv_bot)); - if (!(hs->helpserv = AddService(nick, "+iok", helpserv_conf.description, NULL))) { + if (!(hs->helpserv = AddLocalUser(nick, nick, NULL, helpserv_conf.description, NULL))) { free(hs); return NULL; } @@ -2635,7 +2708,6 @@ static struct helpserv_bot *register_helpserv(const char *nick, const char *help dict_insert(helpserv_bots_bychan_dict, hs->helpchan->name, botlist); } helpserv_botlist_append(botlist, hs); - return hs; } @@ -2643,7 +2715,7 @@ static HELPSERV_FUNC(cmd_register) { char *nick, *helpchan, reason[MAXLEN]; struct handle_info *handle; - REQUIRE_PARMS(4); + REQUIRE_PARMS(3); nick = argv[1]; if (!is_valid_nick(nick)) { helpserv_notice(user, "HSMSG_ILLEGAL_NICK", nick); @@ -2704,7 +2776,7 @@ static void helpserv_free_bot(void *data) { } static void helpserv_unregister(struct helpserv_bot *bot, const char *quit_fmt, const char *global_fmt, const char *actor) { - char reason[MAXLEN], channame[CHANNELLEN], botname[NICKLEN]; + char reason[MAXLEN], channame[CHANNELLEN], *botname; struct helpserv_botlist *botlist; size_t len; @@ -2712,8 +2784,7 @@ static void helpserv_unregister(struct helpserv_bot *bot, const char *quit_fmt, helpserv_botlist_remove(botlist, bot); if (!botlist->used) dict_remove(helpserv_bots_bychan_dict, bot->helpchan->name); - len = strlen(bot->helpserv->nick) + 1; - safestrncpy(botname, bot->helpserv->nick, len); + botname=bot->helpserv->nick; len = strlen(bot->helpchan->name) + 1; safestrncpy(channame, bot->helpchan->name, len); snprintf(reason, sizeof(reason), quit_fmt, actor); @@ -2844,6 +2915,75 @@ static HELPSERV_FUNC(cmd_weekstart) { return changed; } +static HELPSERV_FUNC(cmd_modstats) { + struct handle_info *hi; + struct helpserv_user *victim; + const char *field_name; + int week, mod; + unsigned int *field = NULL; + char *errptr; + + REQUIRE_PARMS(5); + if (!oper_has_access(user, (from_opserv ? opserv : hs->helpserv), helpserv_conf.modstats_level, 0)) + return 0; + if (!(hi = helpserv_get_handle_info(user, argv[1]))) + return 0; + if (!(victim = GetHSUser(hs, hi))) { + helpserv_notice(user, "HSMSG_NOT_IN_USERLIST", hi->handle, hs->helpserv->nick); + return 0; + } + + field_name = argv[2]; + if (!strcasecmp(argv[3], "total")) + week = 4; + else if(!strcasecmp(argv[3], "current")) + week = 0; + else { + week = strtoul(argv[3], &errptr, 0); + if (*errptr != '\0') { + helpserv_notice(user, "HSMSG_MODSTATS_BAD_WEEK"); + return 0; + } + } + mod = strtol(argv[4], NULL, 0); + + if (week < 0 || week > 4) { + helpserv_notice(user, "HSMSG_MODSTATS_BAD_WEEK"); + return 0; + } + + if (!strcasecmp(field_name, "time")) { + if (victim->join_time && (week == 0 || week == 4)) { + victim->time_per_week[0] += now - victim->join_time; + victim->time_per_week[4] += now - victim->join_time; + victim->join_time = now; + } + field = victim->time_per_week; + } + else if (!strcasecmp(field_name, "picked") || !strcasecmp(field_name, "picked_up") || !strcasecmp(field_name, "reqs")) + field = victim->picked_up; + else if (!strcasecmp(field_name, "closed")) + field = victim->closed; + else if (!strcasecmp(field_name, "ra_from") || !strcasecmp(field_name, "reassigned_from")) + field = victim->reassigned_from; + else if (!strcasecmp(field_name, "ra_to") || !strcasecmp(field_name, "reassigned_to")) + field = victim->reassigned_to; + else { + helpserv_notice(user, "HSMSG_MODSTATS_BAD_FIELD"); + return 0; + } + + if (mod < 0 && mod < -(int)field[week]) { + helpserv_notice(user, "HSMSG_MODSTATS_NEGATIVE"); + return 0; + } + + field[week] += mod; + helpserv_notice(user, "HSMSG_MODSTATS_SUCCESS", victim->handle->handle); + + return (mod != 0); +} + static void set_page_target(struct helpserv_bot *hs, enum page_source idx, const char *target) { struct chanNode *new_target, *old_target; @@ -3070,11 +3210,11 @@ static int opt_persist(struct userNode *user, struct helpserv_bot *hs, int from_ helpserv_notice(user, "HSMSG_INVALID_OPTION", argv[0]); return 0; } - hs->persist_types[idx] = new_pers; + hs->persist_lengths[idx] = new_pers; changed = 1; } - helpserv_notice(user, persistence_types[idx].print_name, - user_find_message(user, persistence_lengths[hs->persist_types[idx]].print_name)); + helpserv_notice(user, persistence_lengths[idx].print_name, + user_find_message(user, persistence_lengths[hs->persist_lengths[idx]].print_name)); return changed; } @@ -3162,23 +3302,72 @@ static HELPSERV_OPTION(opt_auto_voice) { OPTION_BINARY(hs->auto_voice, "HSMSG_SET_AUTOVOICE"); } +static HELPSERV_OPTION(opt_auto_join) { + OPTION_BINARY(hs->auto_join, "HSMSG_SET_AUTOJOIN"); +} + static HELPSERV_OPTION(opt_auto_devoice) { OPTION_BINARY(hs->auto_devoice, "HSMSG_SET_AUTODEVOICE"); } +static HELPSERV_OPTION(opt_publicchan) { + char *publicchan; + int changed=0; + if (argc > 0) { + publicchan = argv[0]; + if(strcmp(publicchan, "*")) { + if (!IsChannelName(publicchan)) { + helpserv_notice(user, "HSMSG_ILLEGAL_CHANNEL", publicchan); + HELPSERV_SYNTAX(); + return 0; + } + if (opserv_bad_channel(publicchan)) { + helpserv_notice(user, "HSMSG_ILLEGAL_CHANNEL", publicchan); + return 0; + } + } + if (!hs->publicchan || (hs->publicchan && irccasecmp(hs->publicchan->name, publicchan))) { + if(hs->publicchan) { + //there is another public chan o.O + //part + DelChannelUser(hs->helpserv, hs->publicchan, "unregistered.", 0); + hs->publicchan = NULL; + } + changed = 1; + if(strcmp(publicchan, "*")) { + if (!(hs->publicchan = GetChannel(publicchan))) { + hs->publicchan = AddChannel(publicchan, now, NULL, NULL); + AddChannelUser(hs->helpserv, hs->publicchan)->modes |= MODE_CHANOP; + } else { + struct mod_chanmode change; + mod_chanmode_init(&change); + change.argc = 1; + change.args[0].mode = MODE_CHANOP; + change.args[0].u.member = AddChannelUser(hs->helpserv, hs->publicchan); + mod_chanmode_announce(hs->helpserv, hs->publicchan, &change); + } + } + } + } else { + changed = 0; + } + helpserv_notice(user, "HSMSG_SET_PUBLICCHAN", (hs->publicchan) ? hs->publicchan->name : user_find_message(user,"MSG_NONE")); \ + return changed; +} + static HELPSERV_FUNC(cmd_set) { helpserv_option_func_t *opt; if (argc < 2) { unsigned int i; helpserv_option_func_t *display[] = { - opt_pagetarget_command, opt_pagetarget_alert, opt_pagetarget_status, + opt_publicchan, opt_pagetarget_command, opt_pagetarget_alert, opt_pagetarget_status, opt_pagetype, opt_alert_page_type, opt_status_page_type, opt_greeting, opt_req_opened, opt_req_assigned, opt_req_closed, opt_idle_delay, opt_whine_delay, opt_whine_interval, opt_empty_interval, opt_stale_delay, opt_request_persistence, opt_helper_persistence, opt_notification, opt_id_wrap, - opt_req_maxlen, opt_privmsg_only, opt_req_on_join, opt_auto_voice, + opt_req_maxlen, opt_privmsg_only, opt_req_on_join, opt_auto_voice, opt_auto_join, opt_auto_devoice }; @@ -3396,12 +3585,12 @@ static int request_read_helper(const char *key, void *data, void *extra) { log_module(HS_LOG, LOG_ERROR, "Request %s:%s has a nonexistant opening time. Using time(NULL).", hs->helpserv->nick, key); request->opened = time(NULL); } else { - request->opened = (time_t)strtoul(str, NULL, 0); + request->opened = strtoul(str, NULL, 0); } str = database_get_data(rd->d.object, KEY_REQUEST_ASSIGNED, RECDB_QSTRING); if (str) - request->assigned = (time_t)strtoul(str, NULL, 0); + request->assigned = strtoul(str, NULL, 0); str = database_get_data(rd->d.object, KEY_REQUEST_HELPER, RECDB_QSTRING); if (str) { @@ -3457,7 +3646,7 @@ helpserv_bot_write(const char *key, void *data, void *extra) { saxdb_end_record(ctx); /* Open requests */ - if (hs->persist_types[PERSIST_T_REQUEST] == PERSIST_CLOSE) { + if (hs->persist_lengths[PERSIST_T_REQUEST] == PERSIST_CLOSE) { saxdb_start_record(ctx, KEY_REQUESTS, 0); dict_foreach(hs->requests, request_write_helper, ctx); saxdb_end_record(ctx); @@ -3465,6 +3654,7 @@ helpserv_bot_write(const char *key, void *data, void *extra) { /* Other settings and state */ saxdb_write_string(ctx, KEY_HELP_CHANNEL, hs->helpchan->name); + if(hs->publicchan) saxdb_write_string(ctx, KEY_PUBLIC_CHANNEL, hs->publicchan->name); slist = alloc_string_list(PGSRC_COUNT); for (pagesrc=0; pagesrcpage_targets[pagesrc]; @@ -3487,7 +3677,7 @@ helpserv_bot_write(const char *key, void *data, void *extra) { saxdb_write_int(ctx, interval_types[inttype].db_name, hs->intervals[inttype]); } for (persisttype=0; persisttypepersist_types[persisttype]].db_name; + const char *persist = persistence_lengths[hs->persist_lengths[persisttype]].db_name; saxdb_write_string(ctx, persistence_types[persisttype].db_name, persist); } saxdb_write_string(ctx, KEY_NOTIFICATION, notification_types[hs->notify].db_name); @@ -3500,6 +3690,7 @@ helpserv_bot_write(const char *key, void *data, void *extra) { saxdb_write_int(ctx, KEY_PRIVMSG_ONLY, hs->privmsg_only); saxdb_write_int(ctx, KEY_REQ_ON_JOIN, hs->req_on_join); saxdb_write_int(ctx, KEY_AUTO_VOICE, hs->auto_voice); + saxdb_write_int(ctx, KEY_AUTO_JOIN, hs->auto_join); saxdb_write_int(ctx, KEY_AUTO_DEVOICE, hs->auto_devoice); saxdb_write_int(ctx, KEY_LAST_ACTIVE, hs->last_active); @@ -3520,7 +3711,7 @@ helpserv_saxdb_write(struct saxdb_context *ctx) { static int helpserv_bot_read(const char *key, void *data, UNUSED_ARG(void *extra)) { struct record_data *br = data, *raw_record; struct helpserv_bot *hs; - char *registrar, *helpchannel_name, *str; + char *registrar, *helpchannel_name, *publicchannel_name, *str; dict_t users, requests; enum page_source pagesrc; enum message_type msgtype; @@ -3541,6 +3732,25 @@ static int helpserv_bot_read(const char *key, void *data, UNUSED_ARG(void *extra hs = register_helpserv(key, helpchannel_name, registrar); + publicchannel_name = database_get_data(GET_RECORD_OBJECT(br), KEY_PUBLIC_CHANNEL, RECDB_QSTRING); + if (publicchannel_name) { + if(!IsChannelName(publicchannel_name)) { + log_module(HS_LOG, LOG_ERROR, "%s has an invalid channel name.", key); + return 0; + } else { + if (!(hs->publicchan = GetChannel(publicchannel_name))) { + hs->publicchan = AddChannel(publicchannel_name, now, NULL, NULL); + AddChannelUser(hs->helpserv, hs->publicchan)->modes |= MODE_CHANOP; + } else { + struct mod_chanmode change; + mod_chanmode_init(&change); + change.argc = 1; + change.args[0].mode = MODE_CHANOP; + change.args[0].u.member = AddChannelUser(hs->helpserv, hs->publicchan); + mod_chanmode_announce(hs->helpserv, hs->publicchan, &change); + } + } + } raw_record = dict_find(GET_RECORD_OBJECT(br), KEY_PAGE_DEST, NULL); switch (raw_record ? raw_record->type : RECDB_INVALID) { case RECDB_QSTRING: @@ -3585,13 +3795,13 @@ static int helpserv_bot_read(const char *key, void *data, UNUSED_ARG(void *extra for (persisttype=0; persisttypepersist_types[persisttype] = str ? persistence_from_name(str) : PERSIST_QUIT; + hs->persist_lengths[persisttype] = str ? persistence_from_name(str) : PERSIST_QUIT; } str = database_get_data(GET_RECORD_OBJECT(br), KEY_NOTIFICATION, RECDB_QSTRING); hs->notify = str ? notification_from_name(str) : NOTIFY_NONE; str = database_get_data(GET_RECORD_OBJECT(br), KEY_REGISTERED, RECDB_QSTRING); if (str) - hs->registered = (time_t)strtol(str, NULL, 0); + hs->registered = strtol(str, NULL, 0); str = database_get_data(GET_RECORD_OBJECT(br), KEY_IDWRAP, RECDB_QSTRING); if (str) hs->id_wrap = strtoul(str, NULL, 0); @@ -3607,10 +3817,12 @@ static int helpserv_bot_read(const char *key, void *data, UNUSED_ARG(void *extra hs->req_on_join = str ? enabled_string(str) : 0; str = database_get_data(GET_RECORD_OBJECT(br), KEY_AUTO_VOICE, RECDB_QSTRING); hs->auto_voice = str ? enabled_string(str) : 0; + str = database_get_data(GET_RECORD_OBJECT(br), KEY_AUTO_JOIN, RECDB_QSTRING); + hs->auto_join = str ? enabled_string(str) : 0; str = database_get_data(GET_RECORD_OBJECT(br), KEY_AUTO_DEVOICE, RECDB_QSTRING); hs->auto_devoice = str ? enabled_string(str) : 0; str = database_get_data(GET_RECORD_OBJECT(br), KEY_LAST_ACTIVE, RECDB_QSTRING); - hs->last_active = str ? atoi(str) : now; + hs->last_active = str ? strtoul(str, NULL, 0) : now; dict_foreach(users, user_read_helper, hs); @@ -3631,7 +3843,7 @@ helpserv_saxdb_read(struct dict *conf_db) { } str = database_get_data(conf_db, KEY_LAST_STATS_UPDATE, RECDB_QSTRING); - last_stats_update = str ? (time_t)strtol(str, NULL, 0) : now; + last_stats_update = str ? strtoul(str, NULL, 0) : now; return 0; } @@ -3648,7 +3860,7 @@ static void helpserv_conf_read(void) { helpserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200; str = database_get_data(conf_node, "description", RECDB_QSTRING); - helpserv_conf.description = str; + helpserv_conf.description = str ? str : "Help Queue Manager"; str = database_get_data(conf_node, "reqlogfile", RECDB_QSTRING); if (str && strlen(str)) @@ -3658,6 +3870,8 @@ static void helpserv_conf_read(void) { str = database_get_data(conf_node, "expiration", RECDB_QSTRING); helpserv_conf.expire_age = ParseInterval(str ? str : "60d"); + str = database_get_data(conf_node, "modstats_level", RECDB_QSTRING); + helpserv_conf.modstats_level = str ? strtoul(str, NULL, 0) : 850; str = database_get_data(conf_node, "user_escape", RECDB_QSTRING); helpserv_conf.user_escape = str ? str[0] : '@'; @@ -3672,10 +3886,10 @@ static void helpserv_conf_read(void) { } static struct helpserv_cmd * -helpserv_define_func(const char *name, helpserv_func_t *func, enum helpserv_level access, long flags) { +helpserv_define_func(const char *name, helpserv_func_t *func, enum helpserv_level level, long flags) { struct helpserv_cmd *cmd = calloc(1, sizeof(struct helpserv_cmd)); - cmd->access = access; + cmd->access = level; cmd->weight = 1.0; cmd->func = func; cmd->flags = flags; @@ -3699,13 +3913,15 @@ static void handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason)) { hs = botlist->list[i]; if (!hs->helpserv) continue; - if (hs->persist_types[PERSIST_T_REQUEST] != PERSIST_PART) + if (hs->persist_lengths[PERSIST_T_REQUEST] != PERSIST_PART) continue; for (it=dict_first(hs->requests); it; it=iter_next(it)) { struct helpserv_request *req = iter_data(it); if (mn->user != req->user) + continue; + if (GetUserMode(hs->helpchan, mn->user)) //publicchan continue; if (req->text->used) { helpserv_message(hs, mn->user, MSGTYPE_REQ_DROPPED); @@ -3719,7 +3935,7 @@ static void handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason)) { } } } - + if (mn->user->handle_info && (userlist = dict_find(helpserv_users_byhand_dict, mn->user->handle_info->handle, NULL))) { for (i=0; i < userlist->used; i++) { struct helpserv_user *hs_user = userlist->list[i]; @@ -3742,11 +3958,11 @@ static void handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason)) { for (it=dict_first(hs->requests); it; it=iter_next(it)) { struct helpserv_request *req=iter_data(it); - if ((hs->persist_types[PERSIST_T_HELPER] == PERSIST_PART) + if ((hs->persist_lengths[PERSIST_T_HELPER] == PERSIST_PART) && (req->helper == hs_user)) { - char reason[CHANNELLEN + 8]; - sprintf(reason, "parted %s", mn->channel->name); - helpserv_page_helper_gone(hs, req, reason); + char our_reason[CHANNELLEN + 8]; + sprintf(our_reason, "parted %s", mn->channel->name); + helpserv_page_helper_gone(hs, req, our_reason); } } @@ -3799,7 +4015,7 @@ static void handle_quit(struct userNode *user, UNUSED_ARG(struct userNode *kille for (i=0; i < n; i++) { struct helpserv_request *req = reqlist->list[0]; - if ((req->hs->persist_types[PERSIST_T_REQUEST] == PERSIST_QUIT) || !req->handle) { + if ((req->hs->persist_lengths[PERSIST_T_REQUEST] == PERSIST_QUIT) || !req->handle) { char buf[12]; sprintf(buf, "%lu", req->id); @@ -3833,7 +4049,7 @@ static void handle_quit(struct userNode *user, UNUSED_ARG(struct userNode *kille for (it=dict_first(hs->requests); it; it=iter_next(it)) { struct helpserv_request *req=iter_data(it); - if ((hs->persist_types[PERSIST_T_HELPER] == PERSIST_QUIT) && (req->helper == hs_user)) { + if ((hs->persist_lengths[PERSIST_T_HELPER] == PERSIST_QUIT) && (req->helper == hs_user)) { helpserv_page_helper_gone(hs, req, "disconnected"); } } @@ -3845,8 +4061,7 @@ static void associate_requests_bybot(struct helpserv_bot *hs, struct userNode *u struct helpserv_reqlist *reqlist, *hand_reqlist=NULL; struct helpserv_request *newest=NULL, *nicknewest=NULL; unsigned int i; - const int from_opserv = 0; /* For helpserv_notice */ - + if (!(user->handle_info && (hand_reqlist = dict_find(helpserv_reqs_byhand_dict, user->handle_info->handle, NULL))) && !force_greet) { return; } @@ -3896,6 +4111,7 @@ static void associate_requests_bybot(struct helpserv_bot *hs, struct userNode *u if (!nicknewest || (nicknewest->opened < req->opened)) nicknewest = req; + if (hs->auto_voice && req->helper) { struct mod_chanmode change; @@ -3910,7 +4126,7 @@ static void associate_requests_bybot(struct helpserv_bot *hs, struct userNode *u if ((force_greet && nicknewest) || (newest && (nicknewest == newest))) { /* Let the user know. Either the user is forced to be greeted, or the * above has changed which request will get their next message. */ - helpserv_msguser(user, "HSMSG_GREET_EXISTING_REQ", hs->helpchan->name, nicknewest->id); + //helpserv_msguser(user, "HSMSG_GREET_EXISTING_REQ", hs->helpchan->name, nicknewest->id); } } @@ -3938,7 +4154,7 @@ static int handle_join(struct modeNode *mNode) { if (IsLocal(user)) return 0; - + if (!(botlist = dict_find(helpserv_bots_bychan_dict, chan->name, NULL))) return 0; @@ -3975,7 +4191,7 @@ static int handle_join(struct modeNode *mNode) { if ((reqlist = dict_find(helpserv_reqs_bynick_dict, user->nick, NULL))) { for (j=0; j < reqlist->used; j++) - if (reqlist->list[i]->hs == hs) + if (reqlist->list[j]->hs == hs) break; if (j < reqlist->used) continue; @@ -4031,7 +4247,7 @@ static void handle_nickserv_rename(struct handle_info *handle, const char *old_h for (i=0; i < userlist->used; i++) dict_insert(userlist->list[i]->hs->users, handle->handle, userlist->list[i]); } - + if (reqlist) { for (i=0; i < reqlist->used; i++) { struct helpserv_request *req=reqlist->list[i]; @@ -4105,6 +4321,7 @@ static void handle_nickserv_auth(struct userNode *user, struct handle_info *old_ for (j=1; j <= helper_reqs.used; j++) { struct helpserv_request *req=helper_reqs.list[j-1]; char reqid[12], timestr[MAX_LINE_SIZE]; + time_t feh; tbl.contents[j] = alloca(tbl.width * sizeof(**tbl.contents)); tbl.contents[j][0] = req->hs->helpserv->nick; @@ -4112,7 +4329,8 @@ static void handle_nickserv_auth(struct userNode *user, struct handle_info *old_ tbl.contents[j][1] = strdup(reqid); tbl.contents[j][2] = req->user ? req->user->nick : "Not online"; tbl.contents[j][3] = req->handle ? req->handle->handle : "Not authed"; - strftime(timestr, MAX_LINE_SIZE, HSFMT_TIME, localtime(&req->opened)); + feh = req->opened; + strftime(timestr, MAX_LINE_SIZE, HSFMT_TIME, localtime(&feh)); tbl.contents[j][4] = strdup(timestr); } @@ -4152,7 +4370,7 @@ static void handle_nickserv_auth(struct userNode *user, struct handle_info *old_ struct helpserv_request *req = reqlist->list[i]; struct helpserv_bot *hs=req->hs; - if (!old_handle || hs->persist_types[PERSIST_T_REQUEST] == PERSIST_PART || hs->persist_types[PERSIST_T_REQUEST] == PERSIST_QUIT) { + if (!old_handle || hs->persist_lengths[PERSIST_T_REQUEST] == PERSIST_PART || hs->persist_lengths[PERSIST_T_REQUEST] == PERSIST_QUIT) { /* The request needs to be assigned to the new handle; either it * only persists until part/quit (so it makes sense to keep it as * close to the user as possible, and if it's made persistent later @@ -4176,7 +4394,7 @@ static void handle_nickserv_auth(struct userNode *user, struct handle_info *old_ if (old_handle) { char buf[CHANNELLEN + 14]; - if (hs->persist_types[PERSIST_T_REQUEST] == PERSIST_PART) { + if (hs->persist_lengths[PERSIST_T_REQUEST] == PERSIST_PART) { sprintf(buf, "part channel %s", hs->helpchan->name); } else { strcpy(buf, "quit irc"); @@ -4279,7 +4497,7 @@ static void handle_nickserv_unreg(struct userNode *user, struct handle_info *han } helpserv_reqlist_append(req->parent_nick_list, req); - if (hs->persist_types[PERSIST_T_REQUEST] == PERSIST_CLOSE) + if (hs->persist_lengths[PERSIST_T_REQUEST] == PERSIST_CLOSE) helpserv_msguser(req->user, "HSMSG_REQ_WARN_UNREG", handle->handle, hs->helpchan->name, req->id); } else { if (handle->users) { @@ -4375,7 +4593,7 @@ static void handle_nickserv_failpw(struct userNode *user, struct handle_info *ha } } -static time_t helpserv_next_stats(time_t after_when) { +static unsigned long helpserv_next_stats(time_t after_when) { struct tm *timeinfo = localtime(&after_when); /* This works because mktime(3) says it will accept out-of-range values @@ -4389,15 +4607,17 @@ static time_t helpserv_next_stats(time_t after_when) { } /* If data != NULL, then don't add to the timeq */ -static void helpserv_run_stats(time_t when) { - struct tm when_s; +static void helpserv_run_stats(unsigned long when) { struct helpserv_bot *hs; struct helpserv_user *hs_user; + time_t feh; + unsigned int day; int i; dict_iterator_t it, it2; last_stats_update = when; - localtime_r(&when, &when_s); + feh = when; + day = localtime(&feh)->tm_wday; for (it=dict_first(helpserv_bots_dict); it; it=iter_next(it)) { hs = iter_data(it); @@ -4405,7 +4625,7 @@ static void helpserv_run_stats(time_t when) { hs_user = iter_data(it2); /* Skip the helper if it's not their week-start day. */ - if (hs_user->week_start != when_s.tm_wday) + if (hs_user->week_start != day) continue; /* Adjust their credit if they are in-channel at rollover. */ @@ -4496,6 +4716,7 @@ int helpserv_init() { helpserv_define_func("BOTS", cmd_bots, HlOper, CMD_FROM_OPSERV_ONLY|CMD_IGNORE_EVENT); helpserv_define_func("EXPIRE", cmd_expire, HlOper, CMD_FROM_OPSERV_ONLY); helpserv_define_func("WEEKSTART", cmd_weekstart, HlTrial, CMD_NEED_BOT); + helpserv_define_func("MODSTATS", cmd_modstats, HlOwner, CMD_NEED_BOT); helpserv_option_dict = dict_new(); helpserv_define_option("PAGETARGET", opt_pagetarget_command); @@ -4522,14 +4743,16 @@ int helpserv_init() { helpserv_define_option("PRIVMSGONLY", opt_privmsg_only); helpserv_define_option("REQONJOIN", opt_req_on_join); helpserv_define_option("AUTOVOICE", opt_auto_voice); + helpserv_define_option("AUTOJOIN", opt_auto_join); helpserv_define_option("AUTODEVOICE", opt_auto_devoice); + helpserv_define_option("PUBLICCHAN", opt_publicchan); helpserv_usercmd_dict = dict_new(); dict_insert(helpserv_usercmd_dict, "WAIT", usercmd_wait); helpserv_bots_dict = dict_new(); dict_set_free_data(helpserv_bots_dict, helpserv_free_bot); - + helpserv_bots_bychan_dict = dict_new(); dict_set_free_data(helpserv_bots_bychan_dict, helpserv_botlist_free); @@ -4547,7 +4770,7 @@ int helpserv_init() { /* Make up for downtime... though this will only really affect the * time_per_week */ if (last_stats_update && (helpserv_next_stats(last_stats_update) < now)) { - time_t statsrun = last_stats_update; + unsigned long statsrun = last_stats_update; while ((statsrun = helpserv_next_stats(statsrun)) < now) helpserv_run_stats(statsrun); }