"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 helptickets_exports = {}; __export(helptickets_exports, { BATTLES_REGEX: () => BATTLES_REGEX, HelpTicket: () => HelpTicket, REPLAY_REGEX: () => REPLAY_REGEX, commands: () => commands, getBattleLinks: () => getBattleLinks, getBattleLog: () => getBattleLog, getOpponent: () => getOpponent, handlers: () => handlers, listOpponentsFrom: () => listOpponentsFrom, loginfilter: () => loginfilter, notifyStaff: () => notifyStaff, pages: () => pages, punishmentfilter: () => punishmentfilter, settings: () => settings, textTickets: () => textTickets, tickets: () => tickets, writeSettings: () => writeSettings, writeStats: () => writeStats, writeTickets: () => writeTickets }); module.exports = __toCommonJS(helptickets_exports); var import_lib = require("../../lib"); var import_info = require("../chat-commands/info"); var import_config_loader = require("../config-loader"); var import_helptickets_auto = require("./helptickets-auto"); const TICKET_FILE = "config/tickets.json"; const SETTINGS_FILE = "config/chat-plugins/ticket-settings.json"; const TICKET_CACHE_TIME = 24 * 60 * 60 * 1e3; const TICKET_BAN_DURATION = 48 * 60 * 60 * 1e3; const BATTLES_REGEX = /\bbattle-(?:[a-z0-9]+)-(?:[0-9]+)(?:-[a-z0-9]{31}pw)?/g; const REPLAY_REGEX = new RegExp( `${import_lib.Utils.escapeRegex(Config.routes.replays)}/(?:[a-z0-9]-)?(?:[a-z0-9]+)-(?:[0-9]+)(?:-[a-z0-9]{31}pw)?`, "g" ); const REPORT_NAMECOLORS = { p1: "DodgerBlue", p2: "Crimson", p3: "#FBa92C", p4: "#228B22", other: "#00000" }; Punishments.addPunishmentType({ type: "TICKETBAN", desc: "banned from creating help tickets" }); const defaults = { responses: {} }; const tickets = {}; const settings = (() => { try { return { ...defaults, ...JSON.parse((0, import_lib.FS)(SETTINGS_FILE).readSync()) }; } catch { return { ...defaults }; } })(); try { const ticketData = JSON.parse((0, import_lib.FS)(TICKET_FILE).readSync()); for (const t in ticketData) { const ticket = ticketData[t]; if (ticket.banned) { if (ticket.expires && ticket.expires <= Date.now()) continue; void Punishments.punish(ticket.userid, { type: "TICKETBAN", id: ticket.userid, expireTime: ticket.expires, reason: ticket.reason }, false); delete ticketData[t]; } else { if (ticket.created + TICKET_CACHE_TIME <= Date.now()) { const ticketRoom = Rooms.get(`help-${ticket.userid}`); if (ticketRoom) { const ticketGame = ticketRoom.game; ticketGame.writeStats(false); ticketRoom.expire(); } else if (ticket.text && ticket.open) { ticket.open = false; const startTime = ticket.state?.claimTime || ticket.created; writeStats(`${ticket.type} ${Date.now() - startTime} 0 0 dead valid `); } continue; } if (ticket.open && !Chat.oldPlugins.helptickets) ticket.open = false; tickets[t] = ticket; } } } catch (e) { if (e.code !== "ENOENT") throw e; } function writeTickets() { (0, import_lib.FS)(TICKET_FILE).writeUpdate( () => JSON.stringify(tickets), { throttle: 5e3 } ); } function writeSettings() { (0, import_lib.FS)(SETTINGS_FILE).writeUpdate(() => JSON.stringify(settings)); } async function convertRoomPunishments() { for (const [id, punishment] of Punishments.getPunishments("staff")) { if (punishment.punishType !== "TICKETBAN") continue; Punishments.roomUnpunish("staff", id, "TICKETBAN"); await HelpTicket.ban(id, punishment.reason); } } function writeStats(line) { const date = new Date(); const month = Chat.toTimestamp(date).split(" ")[0].split("-", 2).join("-"); try { Monitor.logPath(`tickets/${month}.tsv`).appendSync(line + "\n"); } catch (e) { if (e.code !== "ENOENT") throw e; } } class HelpTicket extends Rooms.SimpleRoomGame { constructor(room, ticket) { super(room); this.gameid = "helpticket"; this.allowRenames = true; this.involvedStaff = /* @__PURE__ */ new Set(); this.emptyRoom = false; this.firstClaimTime = 0; this.unclaimedTime = 0; this.closeTime = 0; this.resolution = "unknown"; this.result = null; this.room = room; this.room.settings.language = Users.get(ticket.creator)?.language || "english"; this.title = `Help Ticket - ${ticket.type}`; this.ticket = ticket; this.claimQueue = []; this.createTime = Date.now(); this.activationTime = ticket.active ? this.createTime : 0; this.lastUnclaimedStart = ticket.active ? this.createTime : 0; } onJoin(user, connection) { if (!this.ticket.open) return false; if (!user.isStaff || user.id === this.ticket.userid) { if (this.emptyRoom) this.emptyRoom = false; this.addPlayer(user); if (this.ticket.offline) { delete this.ticket.offline; writeTickets(); notifyStaff(); } return false; } if (!this.ticket.claimed) { this.ticket.claimed = user.name; if (!this.firstClaimTime) { this.firstClaimTime = Date.now(); const users = Object.entries(this.room.users).filter( (u) => !(u[1].isStaff && u[1].id !== this.ticket.userid || !u[1].named) ); if (!users.length) this.emptyRoom = true; } if (this.ticket.active) { this.unclaimedTime += Date.now() - this.lastUnclaimedStart; this.lastUnclaimedStart = 0; } tickets[this.ticket.userid] = this.ticket; writeTickets(); this.room.modlog({ action: "TICKETCLAIM", isGlobal: false, loggedBy: user.id }); this.addText(`${user.name} claimed this ticket.`, user); notifyStaff(); } else { this.claimQueue.push(user.name); } } onLeave(user, oldUserid) { const player = this.playerTable[oldUserid || user.id]; if (player) { this.removePlayer(player); this.ticket.offline = true; writeTickets(); notifyStaff(); return; } if (!this.ticket.open) return; if (toID(this.ticket.claimed) === user.id) { if (this.claimQueue.length) { this.ticket.claimed = this.claimQueue.shift() || null; this.room.modlog({ action: "TICKETCLAIM", isGlobal: false, loggedBy: toID(this.ticket.claimed) }); this.addText(`This ticket is now claimed by ${this.ticket.claimed}.`, user); } else { const oldClaimed = this.ticket.claimed; this.ticket.claimed = null; this.lastUnclaimedStart = Date.now(); this.room.modlog({ action: "TICKETUNCLAIM", isGlobal: false, loggedBy: toID(oldClaimed) }); this.addText(`This ticket is no longer claimed.`, user); notifyStaff(); } tickets[this.ticket.userid] = this.ticket; writeTickets(); } else { const index = this.claimQueue.map(toID).indexOf(user.id); if (index > -1) this.claimQueue.splice(index, 1); } } onLogMessage(message, user) { if (!this.ticket.open) return; if (user.isStaff && this.ticket.userid !== user.id) this.involvedStaff.add(user.id); if (this.ticket.active) return; const blockedMessages = [ "hello", "hullo", "hey", "hesrude", "shesrude", "hesinappropriate", "shesinappropriate", "heswore", "sheswore", "help", "yes" ]; if ((!user.isStaff || this.ticket.userid === user.id) && (message.length < 3 || blockedMessages.includes(toID(message)))) { this.room.add(`|c|~Staff|${this.room.tr`Hello! The global staff team would be happy to help you, but you need to explain what's going on first.`}`); this.room.add(`|c|~Staff|${this.room.tr`Please post the information I requested above so a global staff member can come to help.`}`); this.room.update(); return false; } if ((!user.isStaff || this.ticket.userid === user.id) && !this.ticket.active) { this.ticket.active = true; this.activationTime = Date.now(); if (!this.ticket.claimed) this.lastUnclaimedStart = Date.now(); notifyStaff(); this.room.add(`|c|~Staff|${this.room.tr`Thank you for the information, global staff will be here shortly. Please stay in the room.`}`).update(); switch (this.ticket.type) { case "PM Harassment": this.room.add( `|c|~Staff|Global staff might take more than a few minutes to handle your report. If you are being disturbed by another user, you can type \`\`/ignore [username]\`\` in any chat to ignore their messages immediately` ).update(); break; } this.ticket.needsDelayWarning = true; } } forfeit(user) { if (!(user.id in this.playerTable)) return; this.removePlayer(this.playerTable[user.id]); if (!this.ticket.open) return; this.room.modlog({ action: "TICKETABANDON", isGlobal: false, loggedBy: user.id }); this.addText(`${user.name} is no longer interested in this ticket.`, user); if (this.playerCount - 1 > 0) return; this.close(!!this.firstClaimTime, user); return true; } addText(text, user) { if (user) { this.room.addByUser(user, text); } else { this.room.add(text); } this.room.update(); } getButton() { const color = this.ticket.claimed ? `` : this.ticket.offline ? `alt-notifying` : `notifying`; const creator = this.ticket.claimed ? import_lib.Utils.html`${this.ticket.creator}` : import_lib.Utils.html`${this.ticket.creator}`; const user = Users.get(this.ticket.creator); let details = ""; if (user?.namelocked && !this.ticket.state?.namelocked) { if (!this.ticket.state) this.ticket.state = {}; this.ticket.state.namelocked = user.namelocked; } if (this.ticket.state?.namelocked) { details += ` [${this.ticket.state?.namelocked}]`; } if (user?.locked) { const punishment = Punishments.userids.getByType(user.locked, "LOCK"); if (punishment?.rest?.length) { details += ` [${punishment.rest.join(", ")}]`; } } return `Help ${creator}${details}: ${this.ticket.type} `; } getPreview() { if (!this.ticket.active) return `title="The ticket creator has not spoken yet."`; const hoverText = []; const noteBuf = Object.entries(this.ticket.notes || {}).map(([userid, note]) => import_lib.Utils.html`${note} (by ${userid})`).join(" "); const notes = this.ticket.notes ? ` Staff notes: ${noteBuf}` : ""; for (let i = this.room.log.log.length - 1; i >= 0; i--) { const entry = this.room.log.log[i].split("\n")[0].split("|"); entry.shift(); if (!/c:?/.test(entry[0])) continue; if (entry[0] === "c:") entry.shift(); entry.shift(); const user = entry.shift(); let message = entry.join("|"); message = message.startsWith("/log ") ? message.slice(5) : `${user}: ${message}`; hoverText.push(import_lib.Utils.html`${message}`); if (hoverText.length >= 3) break; } if (!hoverText.length) return `title="The ticket creator has not spoken yet.${notes}"`; return `title="${hoverText.reverse().join(` `)}${notes}"`; } close(result, staff) { this.ticket.open = false; tickets[this.ticket.userid] = this.ticket; writeTickets(); this.room.modlog({ action: "TICKETCLOSE", isGlobal: false, loggedBy: staff?.id || "unknown" }); this.addText(staff ? `${staff.name} closed this ticket.` : `This ticket was closed.`, staff); notifyStaff(); this.room.pokeExpireTimer(); for (const ticketGameUser of Object.values(this.playerTable)) { this.removePlayer(ticketGameUser); const user = Users.get(ticketGameUser.id); if (user) user.updateSearch(); } if (!this.involvedStaff.size) { if (staff?.isStaff && staff.id !== this.ticket.userid) { this.involvedStaff.add(staff.id); } else { this.involvedStaff.add(toID(this.ticket.claimed)); } } this.writeStats(result); } writeStats(result) { this.closeTime = Date.now(); if (this.lastUnclaimedStart) this.unclaimedTime += this.closeTime - this.lastUnclaimedStart; if (!this.ticket.active) { this.resolution = "dead"; } else if (!this.firstClaimTime || this.emptyRoom) { this.resolution = "unresolved"; } else { this.resolution = "resolved"; } if (typeof result === "boolean") { switch (this.ticket.type) { case "Appeal": case "IP-Appeal": this.result = result ? "approved" : "denied"; break; case "PM Harassment": case "Battle Harassment": case "Inappropriate Username": case "Inappropriate Pokemon Nicknames": this.result = result ? "valid" : "invalid"; break; case "Public Room Assistance Request": case "Other": default: this.result = result ? "assisted" : "unassisted"; break; } } else { this.result = result; } let firstClaimWait = 0; let involvedStaff = ""; if (this.activationTime) { firstClaimWait = (this.firstClaimTime ? this.firstClaimTime : this.closeTime) - this.activationTime; involvedStaff = Array.from(this.involvedStaff.entries()).map((s) => s[0]).join(","); } const line = `${this.ticket.type} ${this.closeTime - this.createTime} ${firstClaimWait} ${this.unclaimedTime} ${this.resolution} ${this.result} ${involvedStaff}`; writeStats(line); } deleteTicket(staff) { this.close("deleted", staff); this.room.modlog({ action: "TICKETDELETE", isGlobal: false, loggedBy: staff.id }); this.addText(`${staff.name} deleted this ticket.`, staff); delete tickets[this.ticket.userid]; writeTickets(); notifyStaff(); this.room.destroy(); } // Modified version of RoomGame.destory destroy() { if (tickets[this.ticket.userid] && this.ticket.open) { this.ticket.open = false; tickets[this.ticket.userid] = this.ticket; notifyStaff(); writeTickets(); this.writeStats(false); } this.room.game = null; this.room = null; this.setEnded(); for (const player of this.players) player.destroy(); this.players = null; this.playerTable = null; } onChatMessage(message, user) { HelpTicket.uploadReplaysFrom(message, user, user.connections[0]); } // workaround to modlog for no room static async modlog(entry) { await Rooms.Modlog.write("help-texttickets", entry); } static list(sorter) { if (!sorter) { sorter = (ticket) => [ !ticket.offline, ticket.open, ticket.open ? [ticket.active, !ticket.claimed, ticket.created] : 0 ]; } return import_lib.Utils.sortBy(Object.values(tickets), sorter); } static logTextResult(ticket) { const entry = { text: ticket.text, resolved: ticket.resolved, meta: ticket.meta, created: ticket.created, userid: ticket.userid, type: ticket.type, claimed: ticket.claimed, state: ticket.state || {}, recommended: ticket.recommended }; const date = Chat.toTimestamp(new Date()).split(" ")[0]; void Monitor.logPath(`tickets/${date.slice(0, -3)}.jsonl`).append(JSON.stringify(entry) + "\n"); } /** * @param search [search key, search value] (ie ['userid', 'mia'] * returns tickets where the userid property === mia) * If the [value] is omitted (index 1), searches just for tickets with the given property. */ static async getTextLogs(search, date) { if (Config.disableripgrep) { throw new Chat.ErrorMessage("Helpticket logs are currently disabled."); } const results = []; if (await (0, import_config_loader.checkRipgrepAvailability)()) { const searchString = search.length > 1 ? ( // regex escaped to handle things like searching for arrays or objects // (JSON.stringify accounts for " strings are wrapped in and stuff. generally ensures that searching is easier.) import_lib.Utils.escapeRegex(JSON.stringify(search[1]).slice(0, -1)) ) : ""; const args = [ `-e`, search.length > 1 ? `${search[0]}":${searchString}` : `${search[0]}":`, "--no-filename" ]; let lines; try { lines = await import_lib.ProcessManager.exec([ `rg`, Monitor.logPath(`tickets/${date ? `${date}.jsonl` : ""}`).path, ...args ]); } catch (e) { if (e.message.includes("No such file or directory")) { throw new Chat.ErrorMessage(`No ticket logs for that month.`); } if (e.code !== 1 && !e.message.includes("stdout maxBuffer")) { throw e; } if (e.stdout) { lines = e; } else { lines = { stdout: "" }; } } for (const line of lines.stdout.split("\n")) { if (line.trim()) results.push(JSON.parse(line)); } } else { if (!date) throw new Chat.ErrorMessage(`Specify a month.`); const path = Monitor.logPath(`tickets/${date}.jsonl`); if (!path.existsSync()) { throw new Chat.ErrorMessage(`There are no logs for the month "${date}".`); } const stream = path.createReadStream(); for await (const line of stream.byLine()) { if (line.trim()) { const data = JSON.parse(line); const searched = data[search[0]]; let matched = !!searched; if (search[1]) matched = searched === search[1]; if (matched) results.push(data); } } } return results; } static uploadReplaysFrom(text, user, conn) { const rooms = getBattleLinks(text); for (const roomid of rooms) { const room = Rooms.get(roomid); void room?.uploadReplay?.(user, conn, "forpunishment"); } } static colorName(id, info) { for (const k in info.players) { const player = info.players[k]; if (player === id) { return REPORT_NAMECOLORS[k]; } } return REPORT_NAMECOLORS.other; } static formatBattleLog(logs, info, reported) { const log = logs.filter((l) => l.startsWith("|c|")); let buf = ``; for (const line of log) { const [, , username, message] = import_lib.Utils.splitFirst(line, "|", 3); const userid = toID(username); buf += `
`; } if (buf) buf = `You're already in that room.
`; } return buf; }, checker(input) { const room = Rooms.search(input); if (!room) { return [ `That room was not found.`, `Enter either the room name or a room alias.` ]; } if (room.settings.isPrivate) { return ["You may only request help for public rooms."]; } return true; } }, inappokemon: { title: "Please provide replays of the battle with inappropriate Pokemon nicknames", disclaimer: "If the nickname is offensive in a non-english language, or if it's not obvious, please be sure to explain.", checker(input) { if (BATTLES_REGEX.test(input) || REPLAY_REGEX.test(input)) return true; return ["Please provide at least one valid battle or replay URL."]; }, async getReviewDisplay(ticket, staff, conn) { let buf = ``; const [text, context] = ticket.text; let links = getBattleLinks(text); if (context) links.push(...getBattleLinks(context)); const proof = links.join(", "); const opp = getReportedUser(ticket) || (await listOpponentsFrom(ticket))[0]; buf += HelpTicket.displayPunishmentList( ticket.userid, proof, ticket, `Punish ${ticket.userid} (reporter)`, `Battle links given:
`; links = links.filter((url, i) => links.indexOf(url) === i); buf += links.map((uri) => Chat.formatText(`<<${uri}>>`)).join(", "); buf += `
${this.tr`Please to request help.`}
${this.tr`What's going on?`}
`; if (isStaff) { buf += ` `; } else { buf += ` `; } if (!isLast) break; buf += ``; buf += ``; buf += ``; break; case "report": buf += `${this.tr`What do you want to report someone for?`}
`; if (!isLast) break; buf += ``; buf += ``; buf += ``; buf += ``; buf += ``; break; case "pmharassment": buf += `${this.tr`If someone is harassing you in private messages (PMs), click the button below and a global staff member will take a look. If you are being harassed in a chatroom, please ask a room staff member to handle it.`}`;
if (!this.pageid.includes("confirm")) {
buf += ` If it's a minor issue, consider using /ignore [username]
instead.`;
}
buf += `
${this.tr`If someone is harassing you in a battle, click the button below and a global staff member will take a look. If you are being harassed in a chatroom, please ask a room staff member to handle it.`}`;
if (!this.pageid.includes("confirm")) {
buf += ` If it's a minor issue, consider using /ignore [username]
instead.`;
}
buf += `
${this.tr`Please save a replay of the battle if it has ended, or provide a link to the battle if it is still ongoing.`}
`; if (!isLast) break; buf += ``; break; case "inapname": buf += `${this.tr`If a user has an inappropriate name, click the button below and a global staff member will take a look.`}
`; if (!isLast) break; buf += ``; break; case "inappokemon": buf += `${this.tr`If a user has inappropriate Pokemon nicknames, click the button below and a global staff member will take a look.`}
`; buf += `${this.tr`Please save a replay of the battle if it has ended, or provide a link to the battle if it is still ongoing.`}
`; if (!isLast) break; buf += ``; break; case "cheating": buf += `Your opponent cannot control how lucky or unlucky you are, what moves you choose, or the mechanics of the battle. You may just be misunderstanding what happened in your battle!
`; buf += `There are many more situations like this where the opponent was not cheating or hacking. If you're confused about what happened, upload your battle replay and share it with the Help room. They can help you understand what happened!
`; buf += ``; break; case "appeal": if (!isLast) break; if (user.locked || isStaff) { const hostfiltered = user.locked === "#hostfilter" || user.latestHostType === "proxy" && user.locked !== user.id; if (!hostfiltered) { buf += `I want to appeal my lock.
`; const namelocked = user.named && user.id.startsWith("guest"); if (user.locked === user.id || namelocked || isStaff) { if (user.permalocked || isStaff) { buf += ``; } if (!user.permalocked || isStaff) { buf += ``; } } for (const type2 of ["timeleft", "reason", "startedit"]) { buf += ``; } } buf += `I'm locked under a name or IP I don't recognize.
`; if (hostfiltered) { buf += ``; } if (!hostfiltered || isStaff) { for (const type2 of ["public", "homeip", "mobileip", "device"]) { buf += ``; } if (user.locked !== "#hostfilter" && user.latestHostType !== "proxy" && user.locked !== user.id || isStaff) { buf += ``; } } } buf += `I am punished but do not fall under any of the above.
`; if (user.semilocked || isStaff) { buf += ``; } buf += ``; buf += ``; break; case "permalock": buf += `${this.tr`Permalocks are usually for repeated incidents of poor behavior over an extended period of time, and rarely for a single severe infraction. Please keep this in mind when appealing a permalock.`}
`; buf += `${this.tr`Please visit the Discipline Appeals page to appeal your permalock.`}
`; break; case "lock": buf += `${this.tr`If you want to appeal your lock or namelock, click the button below and a global staff member will be with you shortly.`}
`; buf += `You will have to explain in detail why your punishment is unjustified and why we would want to unlock you. Insufficient explanations such as "lol this is bs unlock me" will not be considered.
`; if (!isLast) break; buf += ``; break; case "ip": buf += `${this.tr`If you are locked or namelocked under a name you don't recognize, click the button below to call a global staff member so we can check.`}
`; if (!isLast) break; buf += ``; break; case "homeip": buf += `If you are using your home's wifi network, it means that the person you are being mistaken for did as well (maybe a family member?).
`; buf += `In any case, we have no ability to make the difference - for all we know, you are the same user. Please wait out the lock.
`; break; case "device": buf += `Sorry, but you are considered responsible for whoever has access to your computer.
`; buf += `We have no way to make the difference between two people using the exact same computer. Please wait out the lock.
`; break; case "mobileip": buf += `If you are not the user who was punished, the lock should expire on its own within a few hours.
`; buf += `If you are in a hurry to communicate with another user, you can click on the following button to open a ticket.
`; buf += `A staff member will look at your case as soon as possible.
`; if (!isLast) break; buf += ``; break; case "public": if (user.ips.some((ip) => Punishments.sharedIpBlacklist.has(ip))) { buf += "The public place you are in has had frequent misbehavior. As such, we can not unlock it, to prevent the bad users on it from abusing this. We apologize for the inconvenience.
"; break; } else { buf += `If you have been locked at school or in a library, please write down its name, city and country in the form below so we can verify your claim. This information is strictly confidential, and global staff will only use it to deal with your appeal.
`; buf += `If you have been locked using the wifi of another type of facility, please write down which kind it is in the form.
`; buf += ``; } break; case "timeleft": const expiry = Punishments.checkLockExpiration(user.id); if (typeof expiry !== "string") { buf += `You aren't locked.
`; } else { buf += `Your lock ${expiry.trim().replace("(", "").replace(")", "") || "expires soon"}.`; } break; case "reason": const punishments = Punishments.search(user.id).map((p) => p[2]).filter((t) => ["LOCK", "NAMELOCK"].includes(t.type)); if (!punishments.some((p) => p.reason)) { buf += `No reasons were found on your lock.
`; break; } for (const [idx, punishment] of punishments.entries()) { if (punishments.indexOf(punishment) !== idx) { continue; } else if (punishment.reason) { buf += import_lib.Utils.html`Your ${punishment.type} was for: ${punishment.reason}.
`; } } break; case "startedit": buf += `If you have been locked, it is because your behavior on its own has broken PS rules - whether someone else "started" it does not excuse it.
`; buf += `If someone broke the rules during the interaction with led to your lock, they should have been punished as well when we addressed the report concerning you.
`; break; case "hostfilter": buf += `${this.tr`We automatically lock proxies and VPNs to prevent evasion of punishments and other attacks on our server. To get unlocked, you need to disable your proxy or VPN.`}
`; buf += `${this.tr`If you must use a proxy / VPN to access Pokemon Showdown (e.g. your school blocks the site normally), you will only be able to battle, not chat. When you go home, you will be unlocked and able to freely chat again.`}
`; buf += `For more detailed information, view the proxy help guide.
`; buf += `${this.tr`If you are certain that you are not currently using a proxy / VPN, please continue and open a ticket. Please explain in detail how you are connecting to Pokemon Showdown.`}
`; buf += ``; break; case "semilock": buf += `${this.tr`Do you have an autoconfirmed account? An account is autoconfirmed when it has won at least one rated battle and has been registered for one week or longer.`}
`; if (!isLast) break; buf += ``; break; case "hasautoconfirmed": buf += `
${this.tr`Login to your autoconfirmed account by using the /nick
command in any chatroom, and the semilock will automatically be removed. Afterwards, you can use the /nick
command to switch back to your current username without being semilocked again.`}
${this.tr`If the semilock does not go away, you can try asking a global staff member for help.`}
`; break; case "lacksautoconfirmed": buf += `${this.tr`If you don't have an autoconfirmed account, you will need to contact a global staff member to appeal your semilock.`}
`; break; case "appealother": buf += `${this.tr`Please PM the staff member who punished you. If you don't know who punished you, ask another room staff member; they will redirect you to the correct user. If you are banned or blacklisted from the room, use /roomauth [name of room]
to get a list of room staff members. Bold names are online.`}
${this.tr`Do not PM staff if you are locked (signified by the symbol ‽
in front of your username). Locks are a different type of punishment; to appeal a lock, make a help ticket by clicking the Back button and then selecting the most relevant option.`}
${this.tr`Maybe one of these options will be helpful?`}
`; if (!isLast) break; buf += ``; if (user.trusted || isStaff) buf += ``; buf += ``; break; case "password": buf += `If you need your Pok\xE9mon Showdown password reset, you can fill out a Password Reset Form.
`; buf += `You will need to make a Smogon account to be able to fill out a form.`; break; case "roomhelp": buf += `
${this.tr`If you are a room driver or up in a public room, and you need help watching the chat, one or more global staff members would be happy to assist you!`}
`; buf += ``; break; case "other": buf += `${this.tr`If your issue is not handled above, click the button below to talk to a global staff member. Please be ready to explain the situation.`}
`; if (!isLast) break; buf += ``; break; default: if (!page.startsWith("confirm") || !ticketTitles[page.slice(7)]) { buf += `${this.tr`Malformed help request.`}
`; buf += ``; break; } const type = this.tr(ticketTitles[page.slice(7)]); const submitMeta = import_lib.Utils.splitFirst(meta, "-", 2).join("|"); const textTicket = textTickets[page.slice(7)]; if (textTicket) { buf += `${this.tr(textTicket.title)}
`; if (textTicket.disclaimer) { buf += `${this.tr(textTicket.disclaimer)}
`; } buf += ``; } else { buf += `${this.tr`Are you sure you want to submit a ticket for ${type}?`}
`; buf += ``; } if (textTicket || page.includes("confirmpmharassment")) { buf += ``;
buf += `Global staff might take more than a few minutes to handle your report. `;
buf += `If you are being disturbed by another user, we advise you to type /ignore [username]
in a chatroom to ignore their messages.`;
}
break;
}
}
buf += "