"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var permalocks_exports = {};
__export(permalocks_exports, {
  Nominations: () => Nominations,
  Smogon: () => Smogon,
  commands: () => commands,
  getIPData: () => getIPData,
  pages: () => pages
});
module.exports = __toCommonJS(permalocks_exports);
var import_lib = require("../../lib");
function getIPData(ip) {
  try {
    return (0, import_lib.Net)("https://miapi.dev/api/ip/" + ip).get().then(JSON.parse);
  } catch {
    return null;
  }
}
const Smogon = new class {
  async post(threadNum, postText) {
    if (!Config.smogon)
      return null;
    try {
      const raw = await (0, import_lib.Net)(`https://www.smogon.com/forums/api/posts`).get({
        method: "POST",
        body: new URLSearchParams({
          thread_id: threadNum,
          message: postText
        }).toString(),
        headers: {
          "XF-Api-Key": Config.smogon,
          "Content-Type": "application/x-www-form-urlencoded"
        }
      });
      const data = JSON.parse(raw);
      if (data.errors?.length) {
        const errData = data.errors.pop();
        throw new Error(errData.message);
      }
      return data;
    } catch (e) {
      if (e.message.includes("Not Found")) {
        throw new Error("WHO DELETED THE PERMA THREAD");
      }
      return { error: e.message };
    }
  }
}();
const Nominations = new class {
  constructor() {
    this.noms = [];
    this.icons = {};
    this.load();
  }
  load() {
    try {
      let data = JSON.parse((0, import_lib.FS)("config/chat-plugins/permas.json").readSync());
      if (Array.isArray(data)) {
        data = { noms: data, icons: {} };
        (0, import_lib.FS)("config/chat-plugins/permas.json").writeSync(JSON.stringify(data));
      }
      this.noms = data.noms;
      this.icons = data.icons;
    } catch {
    }
  }
  fetchModlog(id) {
    return Rooms.Modlog.search("global", {
      user: [{ search: id, isExact: true }],
      note: [],
      ip: [],
      action: [],
      actionTaker: []
    }, void 0, true);
  }
  save() {
    (0, import_lib.FS)("config/chat-plugins/permas.json").writeUpdate(() => JSON.stringify({ noms: this.noms, icons: this.icons }));
  }
  notifyStaff() {
    const usRoom = Rooms.get("upperstaff");
    if (!usRoom)
      return;
    usRoom.send(`|uhtml|permanoms|${this.getDisplayButton()}`);
    Chat.refreshPageFor("permalocks", usRoom);
  }
  async add(target, connection) {
    const user = connection.user;
    const [primary, rawAlts, rawIps, type, details] = import_lib.Utils.splitFirst(target, "|", 4).map((f) => f.trim());
    const primaryID = toID(primary);
    const alts = rawAlts.split(",").map(toID).filter(Boolean);
    const ips = rawIps.split(",").map((f) => f.trim()).filter(Boolean);
    for (const ip of ips) {
      if (!IPTools.ipRegex.test(ip))
        this.error(`Invalid IP: ${ip}`, connection);
    }
    const standings = this.getStandings();
    if (!standings[type]) {
      this.error(`Invalid standing: ${type}.`, connection);
    }
    if (!details) {
      this.error("Details must be provided. Explain why this user should be permalocked.", connection);
    }
    if (!primaryID) {
      this.error("A primary username must be provided. Use one of their alts if necessary.", connection);
    }
    for (const nom of this.noms) {
      if (nom.primaryID === primaryID) {
        this.error(`'${primaryID}' was already nominated for permalock by ${nom.by}.`, connection);
      }
    }
    const ipTable = new Set(ips);
    const altTable = /* @__PURE__ */ new Set([...alts]);
    for (const alt of [primaryID, ...alts]) {
      const modlog = await this.fetchModlog(alt);
      if (!modlog || !modlog.results.length)
        continue;
      for (const entry of modlog.results) {
        if (entry.ip)
          ipTable.add(entry.ip);
        if (entry.autoconfirmedID)
          altTable.add(entry.autoconfirmedID);
        if (entry.alts) {
          for (const id of entry.alts)
            altTable.add(id);
        }
      }
    }
    altTable.delete(primaryID);
    this.noms.push({
      by: user.id,
      alts: [...altTable],
      ips: import_lib.Utils.sortBy([...ipTable], (z) => -(IPTools.ipToNumber(z) || Infinity)),
      info: details,
      primaryID,
      standing: type,
      date: Date.now()
    });
    import_lib.Utils.sortBy(this.noms, (nom) => -nom.date);
    this.save();
    this.notifyStaff();
    Rooms.get("staff")?.addByUser(user, `${user.name} submitted a perma nomination for ${primaryID}`);
  }
  find(id) {
    return this.noms.find((f) => f.primaryID === id);
  }
  error(message, conn) {
    conn.popup(message);
    throw new Chat.Interruption();
  }
  close(target, context) {
    const entry = this.find(target);
    if (!entry) {
      this.error(`There is no nomination pending for '${toID(target)}'.`, context.connection);
    }
    this.noms.splice(this.noms.findIndex((f) => f.primaryID === entry.primaryID), 1);
    this.save();
    this.notifyStaff();
    return context.closePage(`permalocks-view-${entry.primaryID}`);
  }
  display(nom, canEdit) {
    let buf = `<div class="infobox">`;
    let title = nom.primaryID;
    if (canEdit) {
      title = `<a href="/view-permalocks-view-${nom.primaryID}" target="_replace">${nom.primaryID}</a>`;
    }
    buf += `<strong>${title}</strong> (submitted by ${nom.by})<br />`;
    buf += `Submitted ${Chat.toTimestamp(new Date(nom.date), { human: true })}<br />`;
    buf += `${Chat.count(nom.alts, "alts")}, ${Chat.count(nom.ips, "IPs")}`;
    buf += `</div>`;
    return buf;
  }
  displayModlog(results) {
    if (!results)
      return "";
    let curDate = "";
    return results.map((result) => {
      const date = new Date(result.time || Date.now());
      const entryRoom = result.visualRoomID || result.roomID || "global";
      let [dateString, timestamp] = Chat.toTimestamp(date, { human: true }).split(" ");
      let line = `<small>[${timestamp}] (${entryRoom})</small> ${result.action}`;
      if (result.userid) {
        line += `: [${result.userid}]`;
        if (result.autoconfirmedID)
          line += ` ac: [${result.autoconfirmedID}]`;
        if (result.alts.length)
          line += ` alts: [${result.alts.join("], [")}]`;
        if (result.ip)
          line += ` [<a href="https://whatismyipaddress.com/ip/${result.ip}" target="_blank">${result.ip}</a>]`;
      }
      if (result.loggedBy)
        line += `: by ${result.loggedBy}`;
      if (result.note)
        line += import_lib.Utils.html`: ${result.note}`;
      if (dateString !== curDate) {
        curDate = dateString;
        dateString = `</p><p>[${dateString}]<br />`;
      } else {
        dateString = ``;
      }
      const thisRoomID = entryRoom?.split(" ")[0];
      if (thisRoomID.startsWith("battle-")) {
        timestamp = `<a href="/${thisRoomID}">${timestamp}</a>`;
      } else {
        const [day, time] = Chat.toTimestamp(date).split(" ");
        timestamp = `<a href="/view-chatlog-${thisRoomID}--${day}--time-${toID(time)}">${timestamp}</a>`;
      }
      return `${dateString}${line}`;
    }).join(`<br />`);
  }
  async displayActionPage(nom) {
    let buf = `<div class="pad">`;
    const standings = this.getStandings();
    buf += `<button class="button" name="send" value="/perma viewnom ${nom.primaryID}" style="float:right">`;
    buf += `<i class="fa fa-refresh"></i> Refresh</button>`;
    buf += `<h3>Nomination: ${nom.primaryID}</h3><hr />`;
    buf += `<strong>By:</strong> ${nom.by} (on ${Chat.toTimestamp(new Date(nom.date))})<br />`;
    buf += `<strong>Recommended punishment:</strong> ${standings[nom.standing]}<br />`;
    buf += `<details class="readmore"><summary><strong>Modlog</strong></summary>`;
    buf += `<div class="infobox limited">`;
    const modlog = await this.fetchModlog(nom.primaryID);
    if (!modlog) {
      buf += `None found.`;
    } else {
      buf += this.displayModlog(modlog.results);
    }
    buf += `</div></details>`;
    if (nom.alts.length) {
      buf += `<details class="readmore"><summary><strong>Listed alts</strong></summary>`;
      for (const [i, alt] of nom.alts.entries()) {
        buf += `- ${alt}: `;
        buf += `<form data-submitsend="/perma standing ${alt},{standing},{reason}">`;
        buf += this.standingDropdown("standing");
        buf += ` <button class="button notifying" type="submit">Change standing</button>`;
        buf += ` <input name="reason" placeholder="Reason" />`;
        buf += `</form>`;
        if (nom.alts[i + 1])
          buf += `<br />`;
      }
      buf += `</details>`;
    }
    if (nom.ips.length) {
      buf += `<details class="readmore"><summary><strong>Listed IPs</strong></summary>`;
      for (const [i, ip] of nom.ips.entries()) {
        const ipData = await getIPData(ip);
        buf += `- <a href="https://whatismyipaddress.com/ip/${ip}">${ip}</a>`;
        if (ipData) {
          buf += `(ISP: ${ipData.isp}, loc: ${ipData.city}, ${ipData.regionName} in ${ipData.country})`;
        }
        buf += `: `;
        buf += `<form data-submitsend="/perma ipstanding ${ip},{standing},{reason}">`;
        buf += this.standingDropdown("standing");
        buf += ` <button class="button notifying" type="submit">Change standing for all users on IP</button>`;
        buf += ` <input name="reason" placeholder="Reason" />`;
        buf += `</form>`;
        if (nom.ips[i + 1])
          buf += `<br />`;
      }
      buf += `</details>`;
    }
    const [matches] = await LoginServer.request("ipmatches", {
      id: nom.primaryID
    });
    if (matches?.results?.length) {
      buf += `<details class="readmore"><summary><strong>Registration IP matches</strong></summary>`;
      for (const [i, { userid, banstate }] of matches.results.entries()) {
        buf += `- ${userid}: `;
        buf += `<form data-submitsend="/perma standing ${userid},{standing}">`;
        buf += this.standingDropdown("standing", banstate + "");
        buf += ` <button class="button notifying" type="submit">Change standing</button></form>`;
        if (matches.results[i + 1])
          buf += `<br />`;
      }
      buf += `</details>`;
    }
    buf += `<p><strong>Staff notes:</strong></p>`;
    buf += `<p><div class="infobox">${Chat.formatText(nom.info).replace(/\n/ig, "<br />")}</div></p>`;
    buf += `<details class="readmore"><summary><strong>Act on primary:</strong></summary>`;
    buf += `<form data-submitsend="/perma actmain ${nom.primaryID},{standing},{note}">`;
    buf += `Standing: ${this.standingDropdown("standing")}`;
    buf += `<br />Notes:<br />`;
    buf += `<textarea name="note" style="width: 100%" cols="50" rows="10"></textarea><br />`;
    buf += `<button class="button notifying" type="submit">Change standing and make post</button>`;
    buf += `</form></details><br />`;
    buf += `<button class="button notifying" name="send" value="/perma resolve ${nom.primaryID}">Mark resolved</button>`;
    return buf;
  }
  standingDropdown(elemName, curStanding = null) {
    let buf = `<select name="${elemName}">`;
    const standings = this.getStandings();
    for (const k in standings) {
      buf += `<option ${curStanding === k ? "disabled" : ""} value="${k}">${standings[k]}</option>`;
    }
    buf += `</select>`;
    return buf;
  }
  getStandings() {
    if (Config.standings)
      return Config.standings;
    Config.standings = {
      "-20": "Confirmed",
      "-10": "Autoconfirmed",
      "0": "New",
      "20": "Permalock",
      "30": "Permaban",
      "100": "Disabled"
    };
    return Config.standings;
  }
  displayAll(canEdit) {
    let buf = `<div class="pad">`;
    buf += `<button class="button" name="send" value="/perma noms" style="float:right"><i class="fa fa-refresh"></i> Refresh</button>`;
    buf += `<h3>Pending perma nominations</h3><hr />`;
    if (!this.noms.length) {
      buf += `None found.`;
      return buf;
    }
    for (const nom of this.noms) {
      buf += this.display(nom, canEdit);
      buf += `<br />`;
    }
    return buf;
  }
  displayNomPage() {
    let buf = `<div class="pad"><h3>Make a nomination for a permanent punishment.</h3><hr />`;
    buf += `<form data-submitsend="/perma submit {primary}|{alts}|{ips}|{type}|{details}">`;
    buf += `<div class="infobox">`;
    buf += `<strong>Primary userid:</strong> <input name="primary" /><br />`;
    buf += `<strong>Alts:</strong><br /><textarea name="alts"></textarea><br /><small>(Separated by commas)</small><br />`;
    buf += `<strong>Static IPs:</strong><br /><textarea name="ips"></textarea><br /><small>(Separated by commas)</small></div><br />`;
    buf += `<strong>Punishment:</strong> `;
    buf += `<select name="type"><option value="20">Permalock</option><option value="30">Permaban</option></select>`;
    buf += `<div class="infobox">`;
    buf += `<strong>Please explain why this user deserves a permanent punishment</strong><br />`;
    buf += `<small>Note: Modlogs are automatically included in review and do not need to be added here.</small><br />`;
    buf += `<textarea style="width: 100%" name="details" cols="50" rows="10"></textarea></div>`;
    buf += `<button class="button notifying" type="submit">Submit nomination</button>`;
    return buf;
  }
  getDisplayButton() {
    const unclaimed = this.noms.filter((f) => !f.claimed);
    let buf = `<div class="infobox">`;
    if (!this.noms.length) {
      buf += `No permalock nominations active.`;
    } else {
      let className = "button";
      if (unclaimed.length)
        className += " notifying";
      buf += `<button class="${className}" name="send" value="/j view-permalocks-list">`;
      buf += `${Chat.count(this.noms.length, "nominations")}`;
      if (unclaimed.length !== this.noms.length) {
        buf += ` (${unclaimed.length} unclaimed)`;
      }
      buf += `</button>`;
    }
    buf += `</div>`;
    return buf;
  }
}();
const commands = {
  perma: {
    ""(target, room, user) {
      this.checkCan("lock");
      if (!user.can("rangeban")) {
        return this.parse(`/j view-permalocks-submit`);
      } else {
        return this.parse(`/j view-permalocks-list`);
      }
    },
    viewnom(target) {
      this.checkCan("rangeban");
      return this.parse(`/j view-permalocks-view-${toID(target)}`);
    },
    submit(target, room, user) {
      this.checkCan("lock");
      return Nominations.add(target, this.connection);
    },
    list() {
      this.checkCan("lock");
      return this.parse(`/j view-permalocks-list`);
    },
    nom() {
      this.checkCan("lock");
      return this.parse(`/j view-permalocks-submit`);
    },
    async actmain(target, room, user) {
      this.checkCan("rangeban");
      const [primaryName, standingName, postReason] = import_lib.Utils.splitFirst(target, ",", 2).map((f) => f.trim());
      const primary = toID(primaryName);
      if (!primary)
        return this.popupReply(`Invalid primary username.`);
      const nom = Nominations.find(primary);
      if (!nom)
        return this.popupReply(`No permalock nomination found for ${primary}.`);
      const standing = parseInt(standingName);
      const standings = Nominations.getStandings();
      if (!standings[standing])
        return this.popupReply(`Invalid standing.`);
      if (!toID(postReason))
        return this.popupReply(`A reason must be given.`);
      const threadNum = Config.permathread;
      if (!threadNum) {
        throw new Chat.ErrorMessage("The link to the perma has not been set - the post could not be made.");
      }
      let postBuf = `[b][url="https://${Config.routes.root}/users/${primary}"]${primary}[/url][/b]`;
      const icon = Nominations.icons[user.id] ? `:${Nominations.icons[user.id]}: - ` : ``;
      postBuf += ` was added to ${standings[standing]} by ${user.name} (${icon}${postReason}).
`;
      postBuf += `Nominated by ${nom.by}.
[spoiler=Nomination notes]${nom.info}[/spoiler]
`;
      postBuf += `${nom.alts.length ? `[spoiler=Alts]${nom.alts.join(", ")}[/spoiler]` : ""}
`;
      if (nom.ips.length) {
        postBuf += `[spoiler=IPs]`;
        for (const ip of nom.ips) {
          const ipData = await getIPData(ip);
          postBuf += `- [url=https://whatismyipaddress.com/ip/${ip}]${ip}[/url]`;
          if (ipData) {
            postBuf += ` (ISP: ${ipData.isp}, loc: ${ipData.city}, ${ipData.regionName} in ${ipData.country})`;
          }
          postBuf += "\n";
        }
        postBuf += `[/spoiler]`;
      }
      const modlog = await Nominations.fetchModlog(nom.primaryID);
      if (modlog?.results.length) {
        let rawHTML = Nominations.displayModlog(modlog.results);
        rawHTML = rawHTML.replace(/<br \/>/g, "\n");
        rawHTML = import_lib.Utils.stripHTML(rawHTML);
        rawHTML = rawHTML.replace(/&#x2f;/g, "/");
        postBuf += `
[spoiler=Modlog]${rawHTML}[/spoiler]`;
      }
      const res = await Smogon.post(
        threadNum,
        postBuf
      );
      if (!res || res.error) {
        return this.popupReply(`Error making post: ${res?.error}`);
      }
      const url = `https://smogon.com/forums/threads/${threadNum}/post-${res.post.post_id}`;
      const result = await LoginServer.request("setstanding", {
        user: primary,
        standing,
        reason: url,
        actor: user.id
      });
      if (result[1]) {
        return this.popupReply(`Error changing standing: ${result[1].message}`);
      }
      nom.post = url;
      this.popupReply(`|html|Standing successfully changed. Smogon post can be found <a href="${url}">at this link</a>.`);
    },
    async standing(target) {
      this.checkCan("rangeban");
      const [name, rawStanding, reason] = import_lib.Utils.splitFirst(target, ",", 2).map((f) => f.trim());
      const id = toID(name);
      if (!id || id.length > 18) {
        return this.popupReply("Invalid username: " + name);
      }
      const standingNum = parseInt(rawStanding);
      if (!standingNum) {
        return this.popupReply(`Invalid standing: ` + rawStanding);
      }
      if (!reason.length) {
        return this.popupReply(`A reason must be given.`);
      }
      const res = await LoginServer.request("setstanding", {
        user: id,
        standing: standingNum,
        reason,
        actor: this.user.id
      });
      if (res[1]) {
        return this.popupReply(`Error in standing change: ` + res[1].message);
      }
      this.popupReply(`Standing successfully changed to ${standingNum} for ${id}.`);
    },
    async ipstanding(target) {
      this.checkCan("rangeban");
      const [ip, standingName, reason] = import_lib.Utils.splitFirst(target, ",", 2).map((f) => f.trim());
      if (!IPTools.ipToNumber(ip)) {
        return this.popupReply(`Invalid IP: ${ip}`);
      }
      const standingNum = parseInt(standingName);
      if (!Config.standings[`${standingNum}`]) {
        return this.popupReply(`Invalid standing: ${standingName}.`);
      }
      if (!reason.length) {
        return this.popupReply("Specify a reason.");
      }
      const res = await LoginServer.request("ipstanding", {
        reason,
        standing: standingNum,
        ip,
        actor: this.user.id
      });
      if (res[1]) {
        return this.popupReply(`Error changing standing: ${res[1].message}`);
      }
      this.popupReply(`All standings on the IP ${ip} changed successfully to ${standingNum}.`);
      this.globalModlog(`IPSTANDING`, null, `${standingNum}${reason ? ` (${reason})` : ""}`, ip);
    },
    resolve(target) {
      this.checkCan("rangeban");
      Nominations.close(target, this);
    },
    seticon(target, room, user) {
      this.checkCan("rangeban");
      let [monName, targetId] = target.split(",");
      if (!targetId)
        targetId = user.id;
      const mon = Dex.species.get(monName);
      if (!mon.exists) {
        return this.errorReply(`Species ${monName} does not exist.`);
      }
      Nominations.icons[targetId] = mon.name.toLowerCase();
      Nominations.save();
      this.sendReply(
        `|html|Updated ${targetId === user.id ? "your" : `${targetId}'s`} permalock post icon to <psicon pokemon='${mon.name.toLowerCase()}' />`
      );
    },
    deleteicon(target, room, user) {
      this.checkCan("rangeban");
      const targetID = toID(target);
      if (!Nominations.icons[targetID]) {
        return this.errorReply(`${targetID} does not have an icon set.`);
      }
      delete Nominations.icons[targetID];
      Nominations.save();
      this.sendReply(`Removed ${targetID}'s permalock post icon.`);
    },
    help: [
      "/perma nom OR /perma - Open the page to make a nomination for a permanent punishment. Requires: % @ &",
      "/perma list - View open nominations. Requires: &",
      "/perma viewnom [userid] - View a nomination for the given [userid]. Requires: &"
    ]
  }
};
const pages = {
  permalocks: {
    list(query, user, conn) {
      this.checkCan("lock");
      this.title = "[Permalock Nominations]";
      return Nominations.displayAll(user.can("rangeban"));
    },
    view(query, user) {
      this.checkCan("rangeban");
      const id = toID(query.shift());
      if (!id)
        return this.errorReply(`Invalid userid.`);
      const nom = Nominations.find(id);
      if (!nom)
        return this.errorReply(`No nomination found for '${id}'.`);
      this.title = `[Perma Nom] ${nom.primaryID}`;
      return Nominations.displayActionPage(nom);
    },
    submit() {
      this.checkCan("lock");
      this.title = "[Perma Nom] Create";
      return Nominations.displayNomPage();
    }
  }
};
process.nextTick(() => {
  Chat.multiLinePattern.register("/perma(noms?)? ");
});
//# sourceMappingURL=permalocks.js.map
