Fix compilation warnings from gcc 4.6.
[srvx.git] / src / mod-helpserv.c
index f1d6b8f2b6116c332986115019f20da2d5052a33..c9eec1b025d4dfec3464f0d0522c0dde1241280e 100644 (file)
@@ -123,6 +123,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." },
@@ -218,6 +222,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 +246,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,6 +481,7 @@ static struct {
     const char *reqlogfile;
     unsigned long db_backup_frequency;
     unsigned int expire_age;
+    unsigned long modstats_level;
     char user_escape;
 } helpserv_conf;
 
@@ -502,7 +508,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 */
 
@@ -629,7 +635,10 @@ static HELPSERV_FUNC(cmd_help);
 /* For messages going to helpers */
 # 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) { \
-        send_message(_target, (helper)->hs->helpserv, ARGS); \
+        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); \
@@ -641,7 +650,10 @@ static HELPSERV_FUNC(cmd_help);
 /* 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) { \
-        send_message(_target, (helper)->hs->helpserv, __VA_ARGS__); \
+        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); \
@@ -702,7 +714,7 @@ 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-%lu-%lu", req->hs->helpserv->nick, (unsigned long)req->opened, req->id);
     if ((res = setjmp(*saxdb_jmp_buf(ctx))) != 0) {
@@ -726,8 +738,7 @@ 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);
     }
 }
 
@@ -848,7 +859,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;
@@ -856,8 +867,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;
@@ -951,32 +962,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);
@@ -1050,7 +1060,7 @@ static void helpserv_usermsg(struct userNode *user, struct helpserv_bot *hs, con
             helpserv_msguser(user, "HSMSG_USERCMD_NO_REQUEST");
             return;
         }
-        if ((hs->persist_types[PERSIST_T_REQUEST] == PERSIST_PART) && !GetUserMode(hs->helpchan, user)) {
+        if ((hs->persist_lengths[PERSIST_T_REQUEST] == PERSIST_PART) && !GetUserMode(hs->helpchan, user)) {
             helpserv_msguser(user, "HSMSG_REQ_YOU_NOT_IN_HELPCHAN_OPEN", hs->helpchan->name);
             return;
         }
@@ -1114,9 +1124,23 @@ static void helpserv_usermsg(struct userNode *user, struct helpserv_bot *hs, con
     }
 
     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");
 }
 
@@ -1338,7 +1362,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);
@@ -1366,7 +1390,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;
@@ -1581,7 +1605,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");
     }
 
@@ -1851,7 +1875,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);
@@ -2005,7 +2029,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;
     }
@@ -2456,7 +2480,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++) {
@@ -2464,7 +2488,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
         }
 
@@ -2880,6 +2904,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;
 
@@ -3106,11 +3199,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;
 }
 
@@ -3493,7 +3586,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);
@@ -3523,7 +3616,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; persisttype<PERSIST_T_COUNT; persisttype++) {
-        const char *persist = persistence_lengths[hs->persist_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);
@@ -3621,7 +3714,7 @@ static int helpserv_bot_read(const char *key, void *data, UNUSED_ARG(void *extra
 
     for (persisttype=0; persisttype<PERSIST_T_COUNT; persisttype++) {
         str = database_get_data(GET_RECORD_OBJECT(br), persistence_types[persisttype].db_name, RECDB_QSTRING);
-        hs->persist_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;
@@ -3694,6 +3787,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] : '@';
 
@@ -3708,10 +3803,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;
@@ -3735,7 +3830,7 @@ 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)) {
@@ -3778,11 +3873,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);
                 }
             }
 
@@ -3835,7 +3930,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);
 
@@ -3869,7 +3964,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");
                 }
             }
@@ -4011,7 +4106,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;
@@ -4190,7 +4285,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
@@ -4214,7 +4309,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");
@@ -4317,7 +4412,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) {
@@ -4536,6 +4631,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);