"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var helptickets_auto_exports = {};
__export(helptickets_auto_exports, {
  actionHandlers: () => actionHandlers,
  addModAction: () => addModAction,
  checkers: () => checkers,
  classifier: () => classifier,
  commands: () => commands,
  determinePunishment: () => determinePunishment,
  getMessageAverages: () => getMessageAverages,
  getModlog: () => getModlog,
  globalModlog: () => globalModlog,
  pages: () => pages,
  punishmentsFor: () => punishmentsFor,
  runPunishments: () => runPunishments,
  settings: () => settings
});
module.exports = __toCommonJS(helptickets_auto_exports);
var import_lib = require("../../lib");
var import_helptickets = require("./helptickets");
var Artemis = __toESM(require("../artemis"));
const ORDERED_PUNISHMENTS = ["WARN", "FORCERENAME", "LOCK", "NAMELOCK", "WEEKLOCK", "WEEKNAMELOCK"];
const PMLOG_IGNORE_TIME = 24 * 60 * 60 * 1e3;
const WHITELIST = ["mia"];
const defaults = {
  punishments: [{
    ticketType: "inapname",
    punishment: "forcerename",
    severity: { type: ["sexual_explicit", "severe_toxicity", "identity_attack"], certainty: 0.4 }
  }, {
    ticketType: "pmharassment",
    punishment: "warn",
    severity: { type: ["sexual_explicit", "severe_toxicity", "identity_attack"], certainty: 0.15 }
  }],
  applyPunishments: false
};
const settings = (() => {
  try {
    return { ...defaults, ...JSON.parse((0, import_lib.FS)("config/chat-plugins/ht-auto.json").readSync()) };
  } catch {
    return defaults;
  }
})();
function saveSettings() {
  return (0, import_lib.FS)("config/chat-plugins/ht-auto.json").writeUpdate(() => JSON.stringify(settings));
}
function visualizePunishment(punishment) {
  const buf = [`punishment: ${punishment.punishment?.toUpperCase()}`];
  buf.push(`ticket type: ${punishment.ticketType}`);
  if (punishment.severity) {
    buf.push(`severity: ${punishment.severity.certainty} (for ${punishment.severity.type.join(", ")})`);
  }
  if (punishment.modlogCount) {
    buf.push(`required modlog: ${punishment.modlogCount}`);
  }
  if (punishment.isSingleMessage) {
    buf.push(`for single messages only`);
  }
  return buf.join(", ");
}
function checkAccess(context) {
  if (!WHITELIST.includes(context.user.id))
    context.checkCan("bypassall");
}
function punishmentsFor(type) {
  return settings.punishments.filter((t) => t.ticketType === type);
}
function supersedes(p1, p2) {
  return ORDERED_PUNISHMENTS.indexOf(p1) > ORDERED_PUNISHMENTS.indexOf(p2);
}
function determinePunishment(ticketType, results, modlog, isSingleMessage = false) {
  const punishments = punishmentsFor(ticketType);
  let action = null;
  const types = [];
  import_lib.Utils.sortBy(punishments, (p) => -ORDERED_PUNISHMENTS.indexOf(p.punishment));
  for (const punishment of punishments) {
    if (isSingleMessage && !punishment.isSingleMessage)
      continue;
    if (punishment.modlogCount && modlog.length < punishment.modlogCount)
      continue;
    if (punishment.severity) {
      let hit = false;
      for (const type of punishment.severity.type) {
        if (results[type] < punishment.severity.certainty)
          continue;
        hit = true;
        types.push(type);
        break;
      }
      if (!hit)
        continue;
    }
    if (!action || supersedes(punishment.punishment, action)) {
      action = punishment.punishment;
    }
  }
  return { action, types };
}
function globalModlog(action, user, note, roomid) {
  user = Users.get(user) || user;
  void Rooms.Modlog.write(roomid || "global", {
    action,
    ip: user && typeof user === "object" ? user.latestIp : void 0,
    userid: toID(user) || void 0,
    loggedBy: "artemis",
    note
  });
}
function addModAction(message) {
  Rooms.get("staff")?.add(`|c|&|/log ${message}`).update();
}
async function getModlog(params) {
  const search = {
    note: [],
    user: [],
    ip: [],
    action: [],
    actionTaker: []
  };
  if (params.user)
    search.user = [{ search: params.user, isExact: true }];
  if (params.ip)
    search.ip = [{ search: params.ip }];
  if (params.actions)
    search.action = params.actions.map((s) => ({ search: s }));
  const res = await Rooms.Modlog.search("global", search);
  return res?.results || [];
}
function closeTicket(ticket, msg) {
  if (!ticket.open)
    return;
  ticket.open = false;
  ticket.active = false;
  ticket.resolved = {
    time: Date.now(),
    by: "the Artemis AI",
    // we want it to be clear to end users that it was not a human
    seen: false,
    staffReason: "",
    result: msg || "",
    note: `Want to learn more about the AI? <a href="https://www.smogon.com/forums/threads/3570628/#post-9056769">Visit the information thread</a>.`
  };
  (0, import_helptickets.writeTickets)();
  (0, import_helptickets.notifyStaff)();
  const tarUser = Users.get(ticket.userid);
  if (tarUser) {
    import_helptickets.HelpTicket.notifyResolved(tarUser, ticket, ticket.userid);
  }
  (0, import_helptickets.writeStats)(`${ticket.type}	${Date.now() - ticket.created}	0	0	resolved	valid	artemis`);
}
async function lock(user, result, ticket, isWeek, isName) {
  const id = toID(user);
  let desc, type;
  const expireTime = isWeek ? Date.now() + 7 * 24 * 60 * 60 * 1e3 : null;
  if (isName) {
    if (typeof user === "object")
      user.resetName();
    desc = "locked your username and prevented you from changing names";
    type = `locked from talking${isWeek ? ` for a week` : ""}`;
    await Punishments.namelock(id, expireTime, null, false, result.reason || "Automatically locked due to a user report");
  } else {
    type = isWeek ? "weeknamelocked" : "namelocked";
    desc = "locked you from talking in chats, battles, and PMing regular users";
    await Punishments.lock(id, expireTime, null, false, result.reason || "Automatically locked due to a user report");
  }
  if (typeof user !== "string") {
    let message = `|popup||html|${user.name} has ${desc} for ${isWeek ? "7" : "2"} days.`;
    if (result.reason)
      message += `

Reason: ${result.reason}`;
    let appeal = "";
    if (Chat.pages.help) {
      appeal += `<a href="view-help-request--appeal"><button class="button"><strong>Appeal your punishment</strong></button></a>`;
    } else if (Config.appealurl) {
      appeal += `appeal: <a href="${Config.appealurl}">${Config.appealurl}</a>`;
    }
    if (appeal)
      message += `

If you feel that your lock was unjustified, you can ${appeal}.`;
    message += `

Your lock will expire in a few days.`;
    user.send(message);
  }
  addModAction(`${id} was ${type} by Artemis. (${result.reason || `report from ${ticket.creator}`})`);
  globalModlog(
    `${isWeek ? "WEEK" : ""}${isName ? "NAME" : ""}LOCK`,
    id,
    (result.reason || `report from ${ticket.creator}`) + (result.proof ? ` PROOF: ${result.proof}` : "")
  );
}
const actionHandlers = {
  forcerename(user, result, ticket) {
    if (typeof user === "string")
      return;
    const id = toID(user);
    user.resetName();
    user.trackRename = id;
    Monitor.forceRenames.set(id, true);
    user.send(
      `|nametaken|Your name was detected to be breaking our name rules. ${result.reason ? `Reason: ${result.reason}. ` : ""}Please change it, or submit a help ticket by typing /ht in chat to appeal this action.`
    );
    Rooms.get("staff")?.add(
      `|html|<span class="username">${id}</span> was automatically forced to choose a new name by Artemis (report from ${ticket.userid}).`
    ).update();
    globalModlog(
      "FORCERENAME",
      id,
      `username determined to be inappropriate due to a report by ${ticket.creator}`,
      result.roomid
    );
    return `${id} was automatically forcerenamed. Thank you for reporting.`;
  },
  async namelock(user, result, ticket) {
    await lock(user, result, ticket, false, true);
    return `${toID(user)} was automatically namelocked. Thank you for reporting.`;
  },
  async weeknamelock(user, result, ticket) {
    await lock(user, result, ticket, true, true);
    return `${toID(user)} was automatically weeknamelocked. Thank you for reporting.`;
  },
  async lock(user, result, ticket) {
    await lock(user, result, ticket);
    return `${toID(user)} was automatically locked. Thank you for reporting.`;
  },
  async weeklock(user, result, ticket) {
    await lock(user, result, ticket, true);
    return `${toID(user)} was automatically weeklocked. Thank you for reporting.`;
  },
  warn(user, result, ticket) {
    user = toID(user);
    user = Users.get(user) || user;
    if (typeof user === "object") {
      user.send(`|c|~|/warn ${result.reason || ""}`);
    } else {
      Punishments.offlineWarns.set(user, result.reason);
    }
    addModAction(
      `${user} was warned by Artemis. ${typeof user === "string" ? "while offline " : ""}(${result.reason || `report from ${ticket.creator}`})`
    );
    globalModlog(
      "WARN",
      user,
      result.reason || `report from ${ticket.creator}`
    );
    return `${user} was automatically warned. Thank you for reporting.`;
  }
};
function shouldNotProcess(message) {
  return (
    // special 'command', blocks things like /log, /raw, /html
    // (but not a // message)
    message.startsWith("/") && !message.startsWith("//") || // broadcasted chat command
    message.startsWith("!")
  );
}
async function getMessageAverages(messages) {
  const counts = {};
  const classified = [];
  for (const message of messages) {
    if (shouldNotProcess(message))
      continue;
    const res = await classifier.classify(message);
    if (!res)
      continue;
    classified.push(res);
    for (const k in res) {
      if (!counts[k])
        counts[k] = { count: 0, raw: 0 };
      counts[k].count++;
      counts[k].raw += res[k];
    }
  }
  const averages = {};
  for (const k in counts) {
    averages[k] = counts[k].raw / counts[k].count;
  }
  return { averages, classified };
}
const checkers = {
  async inapname(ticket) {
    const id = toID(ticket.text[0]);
    const user = Users.getExact(id);
    if (user && !user.trusted) {
      const result = await classifier.classify(user.name);
      if (!result)
        return;
      const keys = ["identity_attack", "sexual_explicit", "severe_toxicity"];
      const matched = keys.some((k) => result[k] >= 0.4);
      if (matched) {
        const modlog = await getModlog({
          ip: user.latestIp,
          actions: ["FORCERENAME", "NAMELOCK", "WEEKNAMELOCK"]
        });
        let { action } = determinePunishment("inapname", result, modlog);
        if (!action)
          action = "forcerename";
        return /* @__PURE__ */ new Map([[user.id, {
          action,
          user,
          result,
          reason: "Username detected to be breaking username rules"
        }]]);
      }
    }
  },
  async inappokemon(ticket) {
    const actions = /* @__PURE__ */ new Map();
    const links = [...(0, import_helptickets.getBattleLinks)(ticket.text[0]), ...(0, import_helptickets.getBattleLinks)(ticket.text[1])];
    for (const link of links) {
      const log = await (0, import_helptickets.getBattleLog)(link);
      if (!log)
        continue;
      for (const [user, pokemon] of Object.entries(log.pokemon)) {
        const userid = toID(user);
        let result = null;
        for (const set of pokemon) {
          if (!set.name)
            continue;
          const results = await classifier.classify(set.name);
          if (!results)
            continue;
          const curAction = determinePunishment("inappokemon", results, []).action;
          if (curAction && (!result || supersedes(curAction, result.action))) {
            result = { action: curAction, name: set.name, result: results, replay: link };
          }
        }
        if (result) {
          actions.set(user, {
            action: result.action,
            user: userid,
            result: result.result,
            reason: `Pokemon name detected to be breaking rules - '${result.name}'`,
            roomid: link
          });
        }
      }
    }
    if (actions.size)
      return actions;
  },
  async battleharassment(ticket) {
    const urls = (0, import_helptickets.getBattleLinks)(ticket.text[0]);
    const actions = /* @__PURE__ */ new Map();
    for (const url of urls) {
      const log = await (0, import_helptickets.getBattleLog)(url);
      if (!log)
        continue;
      const messages = {};
      for (const message of log.log) {
        const [username, text] = import_lib.Utils.splitFirst(message.slice(3), "|").map((f) => f.trim());
        const id = toID(username);
        if (!id)
          continue;
        if (!messages[id])
          messages[id] = [];
        messages[id].push(text);
      }
      for (const [id, messageList] of Object.entries(messages)) {
        const { averages, classified } = await getMessageAverages(messageList);
        const { action } = determinePunishment("battleharassment", averages, []);
        if (action) {
          const existingPunishment = actions.get(id);
          if (!existingPunishment || supersedes(action, existingPunishment.action)) {
            actions.set(id, {
              action,
              user: toID(id),
              result: averages,
              reason: `Not following rules in battles (https://${Config.routes.client}/${url})`,
              proof: urls.join(", ")
            });
          }
        }
        for (const result of classified) {
          const curPunishment = determinePunishment("battleharassment", result, [], true).action;
          if (!curPunishment)
            continue;
          const exists = actions.get(id);
          if (!exists || supersedes(curPunishment, exists.action)) {
            actions.set(id, {
              action: curPunishment,
              user: toID(id),
              result: averages,
              reason: `Not following rules in battles (https://${Config.routes.client}/${url})`,
              proof: urls.join(", ")
            });
          }
        }
      }
    }
    const creatorWasPunished = actions.get(ticket.userid);
    if (creatorWasPunished) {
      let displayReason = "You were punished for your behavior.";
      if (actions.size !== 1) {
        displayReason += ` ${actions.size - 1} other(s) were also punished.`;
      }
      creatorWasPunished.displayReason = displayReason;
    }
    if (actions.size)
      return actions;
  },
  async pmharassment(ticket) {
    const actions = /* @__PURE__ */ new Map();
    const targetId = toID(ticket.text[0]);
    const creator = ticket.userid;
    if (!Config.getpmlog)
      return;
    const pmLog = await Config.getpmlog(targetId, creator);
    const messages = {};
    const ids = /* @__PURE__ */ new Set();
    for (const { from, message, timestamp } of pmLog) {
      if (Date.now() - new Date(timestamp).getTime() > PMLOG_IGNORE_TIME)
        continue;
      const id = toID(from);
      ids.add(id);
      if (!messages[id])
        messages[id] = [];
      messages[id].push(message);
    }
    for (const id of ids) {
      let punishment;
      const { averages, classified } = await getMessageAverages(messages[id]);
      const curPunishment = determinePunishment("pmharassment", averages, []).action;
      if (curPunishment) {
        if (!punishment || supersedes(curPunishment, punishment)) {
          punishment = curPunishment;
        }
        if (punishment) {
          actions.set(id, {
            action: punishment,
            user: id,
            result: {},
            reason: `PM harassment (against ${ticket.userid === id ? targetId : ticket.userid})`
          });
        }
      }
      for (const result of classified) {
        const { action } = determinePunishment("pmharassment", result, [], true);
        if (!action)
          continue;
        const exists = actions.get(id);
        if (!exists || supersedes(action, exists.action)) {
          actions.set(id, {
            action,
            user: id,
            result: {},
            reason: `PM harassment (against ${ticket.userid === id ? targetId : ticket.userid})`
          });
        }
      }
    }
    const creatorWasPunished = actions.get(ticket.userid);
    if (creatorWasPunished) {
      let displayReason = `You were punished for your behavior. `;
      if (actions.has(targetId) && targetId !== ticket.userid) {
        displayReason += ` The person you reported was also punished.`;
      }
      creatorWasPunished.displayReason = displayReason;
    }
    if (actions.size)
      return actions;
  }
};
const classifier = new Artemis.LocalClassifier();
async function runPunishments(ticket, typeId) {
  let result = null;
  if (checkers[typeId]) {
    result = await checkers[typeId](ticket) || null;
  }
  if (result) {
    if (settings.applyPunishments) {
      const responses = [];
      for (const res of result.values()) {
        const curResult = await actionHandlers[res.action.toLowerCase()](res.user, res, ticket);
        if (curResult)
          responses.push([res.action, res.displayReason || curResult]);
        if (toID(res.user) === ticket.creator) {
          closeTicket(ticket, res.displayReason);
        }
      }
      if (responses.length) {
        import_lib.Utils.sortBy(responses, (r) => -ORDERED_PUNISHMENTS.indexOf(r[0]));
        closeTicket(ticket, responses[0][1]);
      } else {
        closeTicket(ticket);
      }
    } else {
      ticket.recommended = [];
      for (const res of result.values()) {
        Rooms.get("abuselog")?.add(
          `|c|&|/log [${ticket.type} Monitor] Recommended: ${res.action}: for ${res.user} (${res.reason})`
        ).update();
        ticket.recommended.push(`${res.action}: for ${res.user} (${res.reason})`);
      }
    }
  }
}
const commands = {
  aht: "autohelpticket",
  autohelpticket: {
    ""() {
      return this.parse(`/help autohelpticket`);
    },
    async test(target) {
      checkAccess(this);
      target = target.trim();
      const response = await classifier.classify(target) || {};
      let buf = import_lib.Utils.html`<strong>Results for "${target}":</strong><br />`;
      buf += `<strong>Score breakdown:</strong><br />`;
      for (const k in response) {
        buf += `&bull; ${k}: ${response[k]}<br />`;
      }
      this.runBroadcast();
      this.sendReplyBox(buf);
    },
    ap: "addpunishment",
    add: "addpunishment",
    addpunishment(target, room, user) {
      checkAccess(this);
      if (!toID(target))
        return this.parse(`/help autohelpticket`);
      const args = Chat.parseArguments(target);
      const punishment = {};
      for (const [k, list] of Object.entries(args)) {
        if (k !== "type" && list.length > 1)
          throw new Chat.ErrorMessage(`More than one ${k} param provided.`);
        const val = list[0];
        switch (k) {
          case "type":
          case "t":
            const types = list.map((f) => f.toLowerCase().replace(/\s/g, "_"));
            for (const type2 of types) {
              if (!Artemis.LocalClassifier.ATTRIBUTES[type2]) {
                return this.errorReply(
                  `Invalid classifier type '${type2}'. Valid types are ` + Object.keys(Artemis.LocalClassifier.ATTRIBUTES).join(", ")
                );
              }
            }
            if (!punishment.severity) {
              punishment.severity = { certainty: 0, type: [] };
            }
            punishment.severity.type.push(...types);
            break;
          case "certainty":
          case "c":
            const num = parseFloat(val);
            if (isNaN(num) || num < 0 || num > 1) {
              return this.errorReply(`Certainty must be a number below 1 and above 0.`);
            }
            if (!punishment.severity) {
              punishment.severity = { certainty: 0, type: [] };
            }
            punishment.severity.certainty = num;
            break;
          case "modlog":
          case "m":
            const count = parseInt(val);
            if (isNaN(count) || count < 0) {
              return this.errorReply(`Modlog count must be a number above 0.`);
            }
            punishment.modlogCount = count;
            break;
          case "ticket":
          case "tt":
          case "tickettype":
            const type = toID(val);
            if (!(type in checkers)) {
              return this.errorReply(
                `The ticket type '${type}' does not exist or is not supported. Supported types are ${Object.keys(checkers).join(", ")}.`
              );
            }
            punishment.ticketType = type;
            break;
          case "p":
          case "punishment":
            const name = toID(val).toUpperCase();
            if (!ORDERED_PUNISHMENTS.includes(name)) {
              return this.errorReply(
                `Punishment '${name}' not supported. Supported punishments: ${ORDERED_PUNISHMENTS.join(", ")}`
              );
            }
            punishment.punishment = name;
            break;
          case "single":
          case "s":
            if (!this.meansYes(toID(val))) {
              return this.errorReply(
                `The 'single' value must always be 'on'. If you don't want it enabled, just do not use this argument type.`
              );
            }
            punishment.isSingleMessage = true;
            break;
        }
      }
      if (!punishment.ticketType) {
        return this.errorReply(`Must specify a ticket type to handle.`);
      }
      if (!punishment.punishment) {
        return this.errorReply(`Must specify a punishment to apply.`);
      }
      if (!(punishment.severity?.certainty && punishment.severity?.type.length)) {
        return this.errorReply(`A severity to monitor for must be specified (certainty).`);
      }
      for (const curP of settings.punishments) {
        let matches = 0;
        for (const k in curP) {
          if (punishment[k] === curP[k]) {
            matches++;
          }
        }
        if (matches === Object.keys(punishment).length) {
          return this.errorReply(`That punishment is already added.`);
        }
      }
      settings.punishments.push(punishment);
      saveSettings();
      this.privateGlobalModAction(
        `${user.name} added a ${punishment.punishment} punishment to the Artemis helpticket handler.`
      );
      this.globalModlog(`AUTOHELPTICKET ADDPUNISHMENT`, null, visualizePunishment(punishment));
    },
    dp: "deletepunishment",
    delete: "deletepunishment",
    deletepunishment(target, room, user) {
      checkAccess(this);
      const num = parseInt(target) - 1;
      if (isNaN(num))
        return this.parse(`/h autohelpticket`);
      const punishment = settings.punishments[num];
      if (!punishment)
        return this.errorReply(`There is no punishment at index ${num + 1}.`);
      settings.punishments.splice(num, 1);
      this.privateGlobalModAction(
        `${user.name} removed the Artemis helpticket ${punishment.punishment} punishment indexed at ${num + 1}`
      );
      this.globalModlog(`AUTOHELPTICKET REMOVE`, null, visualizePunishment(punishment));
    },
    vp: "viewpunishments",
    view: "viewpunishments",
    viewpunishments() {
      checkAccess(this);
      let buf = `<strong>Artemis helpticket punishments</strong><hr />`;
      if (!settings.punishments.length) {
        buf += `None.`;
        return this.sendReplyBox(buf);
      }
      buf += settings.punishments.map(
        (curP, i) => `<strong>${i + 1}:</strong> ${visualizePunishment(curP)}`
      ).join("<br />");
      return this.sendReplyBox(buf);
    },
    togglepunishments(target, room, user) {
      checkAccess(this);
      let message;
      if (this.meansYes(target)) {
        if (settings.applyPunishments) {
          return this.errorReply(`Automatic punishments are already enabled.`);
        }
        settings.applyPunishments = true;
        message = `${user.name} enabled automatic punishments for the Artemis ticket handler`;
      } else if (this.meansNo(target)) {
        if (!settings.applyPunishments) {
          return this.errorReply(`Automatic punishments are already disabled.`);
        }
        settings.applyPunishments = false;
        message = `${user.name} disabled automatic punishments for the Artemis ticket handler`;
      } else {
        return this.errorReply(`Invalid setting. Must be 'on' or 'off'.`);
      }
      this.privateGlobalModAction(message);
      this.globalModlog(`AUTOHELPTICKET TOGGLE`, null, settings.applyPunishments ? "on" : "off");
      saveSettings();
    },
    stats(target) {
      if (!target)
        target = Chat.toTimestamp(new Date()).split(" ")[0];
      return this.parse(`/j view-autohelpticket-stats-${target}`);
    },
    logs(target) {
      if (!target)
        target = Chat.toTimestamp(new Date()).split(" ")[0];
      return this.parse(`/j view-autohelpticket-logs-${target}`);
    },
    resolve(target, room, user) {
      this.checkCan("lock");
      const [ticketId, result] = import_lib.Utils.splitFirst(target, ",").map(toID);
      const ticket = import_helptickets.tickets[ticketId];
      if (!ticket?.open) {
        return this.popupReply(`The user '${ticketId}' does not have a ticket open at present.`);
      }
      if (!["success", "failure"].includes(result)) {
        return this.popupReply(`The result must be 'success' or 'failure'.`);
      }
      (ticket.state || (ticket.state = {})).recommendResult = result;
      (0, import_helptickets.writeTickets)();
      Chat.refreshPageFor(`help-text-${ticketId}`, "staff");
    }
  },
  autohelptickethelp: [
    `/aht addpunishment [args] - Adds a punishment with the given [args]. Requires: whitelist &`,
    `/aht deletepunishment [index] - Deletes the automatic helpticket punishment at [index]. Requires: whitelist &`,
    `/aht viewpunishments - View automatic helpticket punishments. Requires: whitelist &`,
    `/aht togglepunishments [on | off] - Turn [on | off] automatic helpticket punishments. Requires: whitelist &`,
    `/aht stats - View success rates of the Artemis ticket handler. Requires: whitelist &`
  ]
};
const pages = {
  autohelpticket: {
    async stats(query, user) {
      checkAccess(this);
      let month;
      if (query.length) {
        month = /[0-9]{4}-[0-9]{2}/.exec(query.join("-"))?.[0];
      } else {
        month = Chat.toTimestamp(new Date()).split(" ")[0].slice(0, -3);
      }
      if (!month) {
        return this.errorReply(`Invalid month. Must be in YYYY-MM format.`);
      }
      this.title = `[Artemis Ticket Stats] ${month}`;
      this.setHTML(`<div class="pad"><h3>Artemis ticket stats</h3><hr />Searching...`);
      const found = await import_helptickets.HelpTicket.getTextLogs(["recommendResult"], month);
      const percent = (numerator, denom) => Math.floor(numerator / denom * 100);
      let buf = `<div class="pad">`;
      buf += `<button style="float:right;" class="button" name="send" value="/join ${this.pageid}">`;
      buf += `<i class="fa fa-refresh"></i> Refresh</button>`;
      buf += `<h3>Artemis ticket stats</h3><hr />`;
      const dayStats = {};
      const total = { successes: 0, failures: 0, total: 0 };
      const failed = [];
      for (const ticket of found) {
        const day = Chat.toTimestamp(new Date(ticket.created)).split(" ")[0];
        if (!dayStats[day])
          dayStats[day] = { successes: 0, failures: 0, total: 0 };
        dayStats[day].total++;
        total.total++;
        switch (ticket.state.recommendResult) {
          case "success":
            dayStats[day].successes++;
            total.successes++;
            break;
          case "failure":
            dayStats[day].failures++;
            total.failures++;
            failed.push([ticket.userid, ticket.type]);
            break;
        }
      }
      buf += `<strong>Total:</strong> ${total.total}<br />`;
      buf += `<strong>Success rate:</strong> ${percent(total.successes, total.total)}% (${total.successes})<br />`;
      buf += `<strong>Failure rate:</strong> ${percent(total.failures, total.total)}% (${total.failures})<br />`;
      buf += `<strong>Day stats:</strong><br />`;
      buf += `<div class="ladder pad"><table>`;
      let header = "";
      let data = "";
      const sortedDays = import_lib.Utils.sortBy(Object.keys(dayStats), (d) => new Date(d).getTime());
      for (const [i, day] of sortedDays.entries()) {
        const cur = dayStats[day];
        if (!cur.total)
          continue;
        header += `<th>${day.split("-")[2]} (${cur.total})</th>`;
        data += `<td><small>${cur.successes} (${percent(cur.successes, cur.total)}%)`;
        if (cur.failures) {
          data += ` | ${cur.failures} (${percent(cur.failures, cur.total)}%)`;
        } else {
          data += " | 0 (0%)";
        }
        data += "</small></td>";
        if ((i + 1) % 5 === 0 && sortedDays[i + 1]) {
          buf += `<tr>${header}</tr><tr>${data}</tr>`;
          buf += `</div></table>`;
          buf += `<div class="ladder pad"><table>`;
          header = "";
          data = "";
        }
      }
      buf += `<tr>${header}</tr><tr>${data}</tr>`;
      buf += `</div></table>`;
      buf += `<br />`;
      if (failed.length) {
        buf += `<details class="readmore"><summary>Marked as inaccurate</summary>`;
        buf += failed.map(([userid, type]) => `<a href="/view-help-text-${userid}">${userid}</a> (${type})`).join("<br />");
        buf += `</details>`;
      }
      return buf;
    },
    async logs(query, user) {
      checkAccess(this);
      let month;
      if (query.length) {
        month = /[0-9]{4}-[0-9]{2}/.exec(query.join("-"))?.[0];
      } else {
        month = Chat.toTimestamp(new Date()).split(" ")[0].slice(0, -3);
      }
      if (!month) {
        return this.errorReply(`Invalid month. Must be in YYYY-MM format.`);
      }
      this.title = `[Artemis Ticket Logs]`;
      let buf = `<div class="pad"><h3>Artemis ticket logs</h3><hr />`;
      const allHits = await import_helptickets.HelpTicket.getTextLogs(["recommended"], month);
      import_lib.Utils.sortBy(allHits, (h) => -h.created);
      if (allHits.length) {
        buf += `<strong>All hits:</strong><hr />`;
        for (const hit of allHits) {
          if (!hit.recommended)
            continue;
          buf += `<a href="/view-help-text-${hit.userid}">${hit.userid}</a> (${hit.type}) `;
          buf += `[${Chat.toTimestamp(new Date(hit.created))}]<br />`;
          buf += import_lib.Utils.html`&bull; <code><small>${hit.recommended.join(", ")}</small></code><hr />`;
        }
      } else {
        buf += `<div class="message-error">No hits found.</div>`;
      }
      return buf;
    }
  }
};
//# sourceMappingURL=helptickets-auto.js.map
