"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 thing_of_the_day_exports = {}; __export(thing_of_the_day_exports, { commands: () => commands, handlers: () => handlers, otdCommands: () => otdCommands, otdData: () => otdData, otds: () => otds, pages: () => pages, prenoms: () => prenoms, punishmentfilter: () => punishmentfilter }); module.exports = __toCommonJS(thing_of_the_day_exports); var import_lib = require("../../lib"); var import_youtube = require("./youtube"); const MINUTE = 60 * 1e3; const PRENOM_BUMP_TIME = 2 * 60 * MINUTE; const PRENOMS_FILE = "config/chat-plugins/otd-prenoms.json"; const DATA_FILE = "config/chat-plugins/otds.json"; const prenoms = JSON.parse((0, import_lib.FS)(PRENOMS_FILE).readIfExistsSync() || "{}"); const otdData = JSON.parse((0, import_lib.FS)(DATA_FILE).readIfExistsSync() || "{}"); const otds = /* @__PURE__ */ new Map(); const FINISH_HANDLERS = { cotw: async (winner) => { const { channel, nominator } = winner; const searchResults = await import_youtube.YouTube.searchChannel(channel, 1); const result = searchResults?.[0]; if (result) { if (import_youtube.YouTube.data.channels[result]) return; void import_youtube.YouTube.getChannelData(`https://www.youtube.com/channel/${result}`); const yt = Rooms.get("youtube"); if (!yt) return; yt.sendMods( `|c|&|/log The channel with ID ${result} was added to the YouTube channel database.` ); yt.modlog({ action: `ADDCHANNEL`, note: `${result} (${toID(nominator)})`, loggedBy: toID(`COTW`) }); } } }; function savePrenoms() { return (0, import_lib.FS)(PRENOMS_FILE).writeUpdate(() => JSON.stringify(prenoms)); } function toNominationId(nomination) { return nomination.toLowerCase().replace(/\s/g, "").replace(/\b&\b/g, ""); } class OtdHandler { constructor(id, room, settings) { this.id = id; this.name = settings.title; this.room = room; this.nominations = new Map(prenoms[id]); this.removedNominations = /* @__PURE__ */ new Map(); this.voting = false; this.timer = null; this.keys = settings.keys; this.keyLabels = settings.keyLabels; this.timeLabel = settings.timeLabel; this.settings = settings; this.lastPrenom = 0; this.winners = otdData[this.id]?.winners || []; } static create(room, settings) { const { title, timeLabel } = settings; const id = settings.id || toID(title).charAt(0) + "ot" + timeLabel.charAt(0); const handler = new OtdHandler(id, room, settings); otds.set(id, handler); let needsSave = false; for (const winner of handler.winners) { if (winner.timestamp) { winner.time = winner.timestamp; delete winner.timestamp; needsSave = true; } } if (needsSave) handler.save(); return handler; } static parseOldWinners(content, keyLabels, keys) { const data = ("" + content).split("\n"); const winners = []; for (const arg of data) { if (!arg || arg === "\r") continue; if (arg.startsWith(`${keyLabels[0]} `)) { continue; } const entry = {}; const vals = arg.trim().split(" "); for (let i = 0; i < vals.length; i++) { entry[keys[i]] = vals[i]; } entry.time = Number(entry.time) || 0; winners.push(entry); } return winners; } /** * Handles old-format data from the IP and userid refactor */ convertNominations() { for (const value of this.nominations.values()) { if (!Array.isArray(value.userids)) value.userids = Object.keys(value.userids); if (!Array.isArray(value.ips)) value.ips = Object.keys(value.ips); } for (const value of this.removedNominations.values()) { if (!Array.isArray(value.userids)) value.userids = Object.keys(value.userids); if (!Array.isArray(value.ips)) value.ips = Object.keys(value.ips); } } startVote() { this.voting = true; this.timer = setTimeout(() => this.rollWinner(), 20 * MINUTE); this.display(); } finish() { this.voting = false; this.nominations = /* @__PURE__ */ new Map(); this.removedNominations = /* @__PURE__ */ new Map(); delete prenoms[this.id]; void savePrenoms(); if (this.timer) clearTimeout(this.timer); } addNomination(user, nomination) { const id = toNominationId(nomination); if (this.winners.slice(-30).some((entry) => toNominationId(entry[this.keys[0]]) === id)) { return user.sendTo(this.room, `This ${this.name.toLowerCase()} has already been ${this.id} in the past month.`); } for (const value of this.removedNominations.values()) { if (value.userids.includes(toID(user)) || !Config.noipchecks && value.ips.includes(user.latestIp)) { return user.sendTo( this.room, `Since your nomination has been removed by staff, you cannot submit another ${this.name.toLowerCase()} until the next round.` ); } } const prevNom = this.nominations.get(id); if (prevNom) { if (!(prevNom.userids.includes(toID(user)) || !Config.noipchecks && prevNom.ips.includes(user.latestIp))) { return user.sendTo(this.room, `This ${this.name.toLowerCase()} has already been nominated.`); } } for (const [key, value] of this.nominations) { if (value.userids.includes(toID(user)) || !Config.noipchecks && value.ips.includes(user.latestIp)) { user.sendTo(this.room, `Your previous vote for ${value.nomination} will be removed.`); this.nominations.delete(key); if (prenoms[this.id]) { const idx = prenoms[this.id].findIndex((val) => val[0] === key); if (idx > -1) { prenoms[this.id].splice(idx, 1); void savePrenoms(); } } } } const obj = {}; obj[user.id] = user.name; const nomObj = { nomination, name: user.name, userids: user.previousIDs.concat(user.id), ips: user.ips.slice() }; this.nominations.set(id, nomObj); if (!prenoms[this.id]) prenoms[this.id] = []; prenoms[this.id].push([id, nomObj]); void savePrenoms(); user.sendTo(this.room, `Your nomination for ${nomination} was successfully submitted.`); let updateOnly = !this.voting; if (updateOnly) { const now = Date.now(); if (now - this.lastPrenom > PRENOM_BUMP_TIME) { updateOnly = false; this.lastPrenom = now; } } if (this.settings.updateOnNom) { this.display(updateOnly); } } generateNomWindow() { let buffer = ""; if (this.voting) { buffer += `

Nominations for ${this.name} of the ${this.timeLabel} are in progress! Use /${this.id} nom to nominate a${["A", "E", "I", "O", "U"].includes(this.name[0]) ? "n" : ""} ${this.name.toLowerCase()}!

`; if (this.nominations.size) buffer += `Nominations:`; } else { buffer += `

Pre-noms for ${this.name} of the ${this.timeLabel}. Use /${this.id} nom to nominate a${["A", "E", "I", "O", "U"].includes(this.name[0]) ? "n" : ""} ${this.name.toLowerCase()}:

`; } if (this.winners.length) { const winner = this.winners[this.winners.length - 1]; buffer += `

Current winner: ${winner[this.keys[0]]} (nominated by ${winner.nominator})

`; } const entries = []; for (const value of this.nominations.values()) { entries.push(import_lib.Utils.html`
  • ${value.nomination} (Submitted by ${value.name})
  • `); } if (entries.length > 20) { buffer += `
      ${entries.slice(0, Math.ceil(entries.length / 2)).join("")}
      ${entries.slice(Math.ceil(entries.length / 2)).join("")}
    `; } else { buffer += ``; } buffer += `
    `; return buffer; } display(update = false) { if (update) { this.room.uhtmlchange("otd", this.generateNomWindow()); } else { this.room.add(`|uhtml|otd|${this.generateNomWindow()}`); } } displayTo(connection) { connection.sendTo(this.room, `|uhtml|otd|${this.generateNomWindow()}`); } rollWinner() { const keys = [...this.nominations.keys()]; if (!keys.length) return false; const winner = this.nominations.get(keys[Math.floor(Math.random() * keys.length)]); if (!winner) return false; const winnerEntry = this.appendWinner(winner.nomination, winner.name); const names = [...this.nominations.values()].map((obj) => obj.name); const columns = names.length > 27 ? 4 : names.length > 18 ? 3 : names.length > 9 ? 2 : 1; let content = ""; for (let i = 0; i < columns; i++) { content += `${names.slice(Math.ceil(i / columns * names.length), Math.ceil((i + 1) / columns * names.length)).join("
    ")}`; } const namesHTML = `${content}

    `; const finishHandler = FINISH_HANDLERS[this.id]; if (finishHandler) { void finishHandler(winnerEntry); } this.room.add( import_lib.Utils.html`|uhtml|otd|

    ` + `Nominations for ${this.name} of the ${this.timeLabel} are over!

    Out of ${keys.length} nominations, we randomly selected ${winner.nomination} as the winner!(Nomination by ${winner.name})

    Thanks to today's participants:` + namesHTML ); this.room.update(); this.finish(); return true; } removeNomination(name) { name = toID(name); let success = false; for (const [key, value] of this.nominations) { if (value.userids.includes(name)) { this.removedNominations.set(key, value); this.nominations.delete(key); if (prenoms[this.id]) { const idx = prenoms[this.id].findIndex((val) => val[0] === key); if (idx > -1) { prenoms[this.id].splice(idx, 1); void savePrenoms(); } } success = true; } } if (this.voting) this.display(); return success; } forceWinner(winner, user) { this.appendWinner(winner, user); this.finish(); } appendWinner(nomination, user) { const entry = { time: Date.now(), nominator: user }; entry[this.keys[0]] = nomination; this.winners.push(entry); this.save(); return entry; } removeWinner(nominationName) { for (const [i, entry] of this.winners.entries()) { if (toID(entry[this.keys[0]]) === toID(nominationName)) { const removed = this.winners.splice(i, 1); this.save(); return removed[0]; } } throw new Chat.ErrorMessage(`The winner with nomination ${nominationName} could not be found.`); } setWinnerProperty(properties) { if (!this.winners.length) return; for (const i in properties) { this.winners[this.winners.length - 1][i] = properties[i]; } return this.save(); } save(destroy = false) { if (!destroy) { otdData[this.id] = { settings: this.settings, winners: this.winners }; } (0, import_lib.FS)(DATA_FILE).writeUpdate(() => JSON.stringify(otdData)); } destroy() { this.room = null; delete otdData[this.id]; otds.delete(this.id); delete Chat.commands[this.id]; delete Chat.pages[this.id]; this.save(true); } async generateWinnerDisplay() { if (!this.winners.length) return false; const winner = this.winners[this.winners.length - 1]; let output = import_lib.Utils.html`

    ` + `

    The ${this.name} of the ${this.timeLabel} is ${winner[this.keys[0]]}${winner.author ? ` by ${winner.author}` : ""}.`; if (winner.quote) output += import_lib.Utils.html`
    "${winner.quote}"`; if (winner.tagline) output += import_lib.Utils.html`
    ${winner.tagline}`; output += `

    `; if (winner.image) { try { const [width, height] = await Chat.fitImage(winner.image, 100, 100); output += import_lib.Utils.html``; } catch { } } output += `
    `; if (winner.event) output += import_lib.Utils.html`Event: ${winner.event}
    `; if (winner.song) { output += `Song: `; if (winner.link) { output += import_lib.Utils.html`${winner.song}`; } else { output += import_lib.Utils.escapeHTML(winner.song); } output += `
    `; } else if (winner.link) { output += import_lib.Utils.html`Link: ${winner.link}
    `; } const athleteDetails = []; if (winner.sport) athleteDetails.push(import_lib.Utils.html`Sport: ${winner.sport}`); if (winner.team) athleteDetails.push(import_lib.Utils.html`Team: ${winner.team}`); if (winner.age) athleteDetails.push(import_lib.Utils.html`Age: ${winner.age}`); if (winner.country) athleteDetails.push(import_lib.Utils.html`Nationality: ${winner.country}`); if (athleteDetails.length) { output += athleteDetails.slice(0, 2).join(" | ") + "
    "; if (athleteDetails.length > 2) output += athleteDetails.slice(2).join(" | ") + "
    "; } output += import_lib.Utils.html`Nominated by ${winner.nominator}.`; output += `
    `; return output; } generateWinnerList(context) { context.title = `${this.id.toUpperCase()} Winners`; let buf = `

    ${this.name} of the ${this.timeLabel} Winners

    `; const columns = []; const labels = []; for (let i = 0; i < this.keys.length; i++) { if (i === 0 || ["song", "event", "link", "tagline", "sport", "country"].includes(this.keys[i]) && !(this.keys[i] === "link" && this.keys.includes("song"))) { columns.push(this.keys[i]); labels.push(this.keyLabels[i]); } } if (!columns.includes("time")) { columns.push("time"); labels.push("Timestamp"); } let content = ``; content += `${labels.map((label) => `

    ${label}

    `).join("")}`; for (let i = this.winners.length - 1; i >= 0; i--) { const entry = columns.map((col) => { let val = this.winners[i][col]; if (!val) return ""; switch (col) { case "time": const date = new Date(parseInt(this.winners[i].time)); const pad = (num) => num < 10 ? "0" + num : num; return import_lib.Utils.html`${pad(date.getMonth() + 1)}-${pad(date.getDate())}-${date.getFullYear()}`; case "song": if (!this.winners[i].link) return val; case "link": return `${val}`; case "book": val = `${val}${this.winners[i].author ? ` by ${this.winners[i].author}` : ""}`; case columns[0]: return `${import_lib.Utils.escapeHTML(val)}${this.winners[i].nominator ? import_lib.Utils.html`
    nominated by ${this.winners[i].nominator}` : ""}`; default: return import_lib.Utils.escapeHTML(val); } }); content += `${entry.map((val) => `${val}`).join("")}`; } if (!content) { buf += `

    There have been no ${this.id} winners.

    `; } else { buf += `${content}
    `; } buf += `
    `; return buf; } } function selectHandler(message) { const id = toID(message.substring(1).split(" ")[0]); const handler = otds.get(id); if (!handler) throw new Error("Invalid type for otd handler."); return handler; } const otdCommands = { start(target, room, user, connection, cmd) { this.checkChat(); const handler = selectHandler(this.message); if (!handler.room) return this.errorReply(`The room for this -otd doesn't exist.`); if (room !== handler.room) return this.errorReply(`This command can only be used in ${handler.room.title}.`); this.checkCan("mute", null, room); if (handler.voting) { return this.errorReply( `The nomination for the ${handler.name} of the ${handler.timeLabel} nomination is already in progress.` ); } handler.startVote(); this.privateModAction(`${user.name} has started nominations for the ${handler.name} of the ${handler.timeLabel}.`); this.modlog(`${handler.id.toUpperCase()} START`, null); }, starthelp: [`/-otd start - Starts nominations for the Thing of the Day. Requires: % @ # &`], end(target, room, user) { this.checkChat(); const handler = selectHandler(this.message); if (!handler.room) return this.errorReply(`The room for this -otd doesn't exist.`); if (room !== handler.room) return this.errorReply(`This command can only be used in ${handler.room.title}.`); this.checkCan("mute", null, room); if (!handler.voting) { return this.errorReply(`There is no ${handler.name} of the ${handler.timeLabel} nomination in progress.`); } if (!handler.nominations.size) { return this.errorReply(`Can't select the ${handler.name} of the ${handler.timeLabel} without nominations.`); } handler.rollWinner(); this.privateModAction(`${user.name} has ended nominations for the ${handler.name} of the ${handler.timeLabel}.`); this.modlog(`${handler.id.toUpperCase()} END`, null); }, endhelp: [ `/-otd end - End nominations for the Thing of the Day and set it to a randomly selected nomination. Requires: % @ # &` ], nom(target, room, user) { this.checkChat(target); if (!target) return this.parse("/help otd"); const handler = selectHandler(this.message); if (!handler.room) return this.errorReply(`The room for this -otd doesn't exist.`); if (room !== handler.room) return this.errorReply(`This command can only be used in ${handler.room.title}.`); if (!toNominationId(target).length || target.length > 75) { return this.sendReply(`'${target}' is not a valid ${handler.name.toLowerCase()} name.`); } handler.addNomination(user, target); }, nomhelp: [`/-otd nom [nomination] - Nominate something for Thing of the Day.`], view(target, room, user, connection) { this.checkChat(); if (!this.runBroadcast()) return false; const handler = selectHandler(this.message); if (!handler.room) return this.errorReply(`The room for this -otd doesn't exist.`); if (room !== handler.room) return this.errorReply(`This command can only be used in ${handler.room.title}.`); if (this.broadcasting) { selectHandler(this.message).display(); } else { selectHandler(this.message).displayTo(connection); } }, viewhelp: [`/-otd view - View the current nominations for the Thing of the Day.`], remove(target, room, user) { this.checkChat(); const handler = selectHandler(this.message); if (!handler.room) return this.errorReply(`The room for this -otd doesn't exist.`); if (room !== handler.room) return this.errorReply(`This command can only be used in ${handler.room.title}.`); this.checkCan("mute", null, room); const userid = toID(target); if (!userid) return this.errorReply(`'${target}' is not a valid username.`); if (handler.removeNomination(userid)) { this.privateModAction(`${user.name} removed ${target}'s nomination for the ${handler.name} of the ${handler.timeLabel}.`); this.modlog(`${handler.id.toUpperCase()} REMOVENOM`, userid); } else { this.sendReply(`User '${target}' has no nomination for the ${handler.name} of the ${handler.timeLabel}.`); } }, removehelp: [ `/-otd remove [username] - Remove a user's nomination for the Thing of the Day.`, `Prevents them from voting again until the next round. Requires: % @ # &` ], removewinner(target, room, user) { const handler = selectHandler(this.message); room = this.requireRoom(handler.room.roomid); this.checkCan("mute", null, room); if (!toID(target)) { return this.parse(`/help aotd removewinner`); } const removed = handler.removeWinner(target); this.privateModAction(`${user.name} removed the nomination for ${removed[handler.keys[0]]} from ${removed.nominator}`); this.modlog(`${handler.id.toUpperCase()} REMOVEWINNER`, removed.nominator, removed[handler.keys[0]]); }, removewinnerhelp: [ `/-otd removewinner [nomination] - Remove winners matching the given [nomination] from Thing of the Day.`, `Requires: % @ # &` ], force(target, room, user) { this.checkChat(); if (!target) return this.parse("/help aotd force"); const handler = selectHandler(this.message); if (!handler.room) return this.errorReply(`The room for this -otd doesn't exist.`); if (room !== handler.room) return this.errorReply(`This command can only be used in ${handler.room.title}.`); this.checkCan("declare", null, room); if (!toNominationId(target).length || target.length > 50) { return this.sendReply(`'${target}' is not a valid ${handler.name.toLowerCase()} name.`); } handler.forceWinner(target, user.name); this.privateModAction(`${user.name} forcibly set the ${handler.name} of the ${handler.timeLabel} to ${target}.`); this.modlog(`${handler.id.toUpperCase()} FORCE`, user.name, target); room.add(`The ${handler.name} of the ${handler.timeLabel} was forcibly set to '${target}'`); }, forcehelp: [ `/-otd force [nomination] - Forcibly sets the Thing of the Day without a nomination round. Requires: # &` ], delay(target, room, user) { this.checkChat(); const handler = selectHandler(this.message); if (!handler.room) return this.errorReply(`The room for this -otd doesn't exist.`); if (room !== handler.room) return this.errorReply(`This command can only be used in ${handler.room.title}.`); this.checkCan("mute", null, room); if (!(handler.voting && handler.timer)) { return this.errorReply(`There is no ${handler.name} of the ${handler.timeLabel} nomination to disable the timer for.`); } clearTimeout(handler.timer); this.privateModAction(`${user.name} disabled the ${handler.name} of the ${handler.timeLabel} timer.`); }, delayhelp: [ `/-otd delay - Turns off the automatic 20 minute timer for Thing of the Day voting rounds. Requires: % @ # &` ], set(target, room, user) { this.checkChat(); const handler = selectHandler(this.message); if (!handler.room) return this.errorReply(`The room for this -otd doesn't exist.`); if (room !== handler.room) return this.errorReply(`This command can only be used in ${handler.room.title}.`); this.checkCan("mute", null, room); const params = target.split(target.includes("|") ? "|" : ",").map((param) => param.trim()); const changelist = {}; for (const param of params) { let [key, ...values] = param.split(":"); if (!key || !values.length) return this.errorReply(`Syntax error in '${param}'`); key = key.trim(); const value = values.join(":").trim(); if (!handler.keys.includes(key)) { return this.errorReply(`Invalid key: '${key}'. Valid keys: ${handler.keys.join(", ")}`); } switch (key) { case "artist": case "film": case "show": case "channel": case "book": case "author": case "athlete": if (!toNominationId(value) || value.length > 50) return this.errorReply(`Please enter a valid ${key} name.`); break; case "quote": case "tagline": case "match": case "event": case "videogame": if (!value.length || value.length > 150) return this.errorReply(`Please enter a valid ${key}.`); break; case "sport": case "team": case "song": case "country": if (!value.length || value.length > 50) return this.errorReply(`Please enter a valid ${key} name.`); break; case "link": case "image": if (!/https?:\/\//.test(value)) { return this.errorReply(`Please enter a valid URL for the ${key} (starting with http:// or https://)`); } if (value.length > 200) return this.errorReply("URL too long."); break; case "age": const num = parseInt(value); if (isNaN(num) || num < 1 || num > 100) return this.errorReply("Please enter a valid number as an age"); break; default: if (!toNominationId(value)) { return this.errorReply(`No value provided for key ${key}.`); } break; } changelist[key] = value; } const keys = Object.keys(changelist); if (keys.length) { void handler.setWinnerProperty(changelist); this.modlog(handler.id.toUpperCase(), null, `changed ${keys.join(", ")}`); return this.privateModAction(`${user.name} changed the following propert${Chat.plural(keys, "ies", "y")} of the ${handler.name} of the ${handler.timeLabel}: ${keys.join(", ")}`); } }, sethelp: [ `/-otd set property: value[, property: value] - Set the winner, quote, song, link or image for the current Thing of the Day.`, `Requires: % @ # &` ], toggleupdate(target, room, user) { const otd = selectHandler(this.message); room = this.requireRoom(otd.room.roomid); this.checkCan("declare", null, room); let logMessage = ""; if (this.meansYes(target)) { if (otd.settings.updateOnNom) { throw new Chat.ErrorMessage(`This -OTD is already set to update automatically on nomination.`); } otd.settings.updateOnNom = true; logMessage = "update automatically on nomination"; } else { if (!otd.settings.updateOnNom) { throw new Chat.ErrorMessage(`This -OTD is not set to update automatically on nomination.`); } delete otd.settings.updateOnNom; logMessage = "not update on nomination"; } this.privateModAction(`${user.name} set the ${otd.name} of the ${otd.timeLabel} to ${logMessage}`); this.modlog(`OTD TOGGLEUPDATE`, null, logMessage); otd.save(); }, winners(target, room, user, connection) { this.checkChat(); const handler = selectHandler(this.message); if (!handler.room) return this.errorReply(`The room for this -otd doesn't exist.`); if (room !== handler.room) return this.errorReply(`This command can only be used in ${handler.room.title}.`); return this.parse(`/join view-${handler.id}`); }, winnershelp: [`/-otd winners - Displays a list of previous things of the day.`], async ""(target, room) { this.checkChat(); if (!this.runBroadcast()) return false; const handler = selectHandler(this.message); if (!handler.room) return this.errorReply(`The room for this -otd doesn't exist.`); if (room !== handler.room) return this.errorReply(`This command can only be used in ${handler.room.title}.`); const text = await handler.generateWinnerDisplay(); if (!text) return this.errorReply("There is no winner yet."); this.sendReplyBox(text); } }; const pages = {}; const commands = { otd: { create(target, room, user) { room = this.requireRoom(); if (room.settings.isPrivate) { return this.errorReply(`This command is only available in public rooms`); } const count = [...otds.values()].filter((otd2) => otd2.room.roomid === room.roomid).length; if (count > 3) { return this.errorReply(`This room already has 3+ -otd's.`); } this.checkCan("rangeban"); if (!toID(target)) { return this.parse(`/help otd`); } const [title, time, ...keyLabels] = target.split(",").map((i) => i.trim()); if (!toID(title)) { return this.errorReply(`Invalid title.`); } const timeLabel = toID(time); if (!["week", "day"].includes(timeLabel)) { return this.errorReply("Invalid time label - use 'week' or 'month'"); } const id = `${title.charAt(0)}ot${timeLabel.charAt(0)}`; const existing = otds.get(id); if (existing) { this.errorReply(`That -OTD already exists (${existing.name} of the ${existing.timeLabel}, in ${existing.room.title})`); return this.errorReply(`Try picking a new title.`); } const titleIdx = keyLabels.map(toID).indexOf(toID(title)); if (titleIdx > -1) { keyLabels.splice(titleIdx, 1); } keyLabels.unshift(title); const filteredKeys = keyLabels.map(toNominationId).filter(Boolean); if (!filteredKeys.length) { return this.errorReply(`No valid key labels given.`); } if (new Set(filteredKeys).size !== keyLabels.length) { return this.errorReply(`Invalid keys in set - do not use duplicate key labels.`); } if (filteredKeys.length < 3) { return this.errorReply(`Specify at least 3 key labels.`); } if (filteredKeys.some((k) => k.length < 3 || k.length > 50)) { return this.errorReply(`All labels must be more than 3 characters and less than 50 characters long.`); } const otd = OtdHandler.create(room, { keyLabels, keys: filteredKeys, title, timeLabel, roomid: room.roomid }); const name = `${otd.name} of the ${otd.timeLabel}`; this.globalModlog(`OTD CREATE`, null, `${name} - ${filteredKeys.join(", ")}`); this.privateGlobalModAction(`${user.name} created the ${name} for ${room.title}`); otd.save(); }, updateroom(target, room, user) { this.checkCan("rangeban"); const [otdId, roomid] = target.split(",").map((i) => toID(i)); if (!otdId || !roomid) { return this.parse("/help otd"); } const otd = otds.get(otdId); if (!otd) { return this.errorReply(`OTD ${otd} not found.`); } const targetRoom = Rooms.get(roomid); if (!targetRoom) { return this.errorReply(`Room ${roomid} not found.`); } const oldRoom = otd.settings.roomid.slice(); otd.settings.roomid = targetRoom.roomid; otd.room = targetRoom; otd.save(); this.privateGlobalModAction( `${user.name} updated the room for the ${otd.name} of the ${otd.timeLabel} from ${oldRoom} to ${targetRoom}` ); this.globalModlog(`OTD UPDATEROOM`, null, `${otd.id} to ${targetRoom} from ${oldRoom}`); }, delete(target, room, user) { this.checkCan("rangeban"); target = toID(target); if (!target) { return this.parse(`/help otd`); } const otd = otds.get(target); if (!otd) return this.errorReply(`OTD ${target} not found.`); otd.destroy(); this.globalModlog(`OTD DELETE`, null, target); this.privateGlobalModAction(`${user.name} deleted the OTD ${otd.name} of the ${otd.timeLabel}`); } }, otdhelp: [ `/otd create [title], [time], [...labels] - Creates a Thing of the Day with the given [name], [time], and [labels]. Requires: &`, `/otd updateroom [otd], [room] - Updates the room for the given [otd] to the new [room]. Requires: &`, `/otd delete [otd] - Removes the given Thing of the Day. Requires: &` ] }; const otdHelp = [ `Thing of the Day plugin commands (aotd, fotd, sotd, cotd, botw, motw, anotd):`, `- /-otd - View the current Thing of the Day.`, `- /-otd start - Starts nominations for the Thing of the Day. Requires: % @ # &`, `- /-otd nom [nomination] - Nominate something for Thing of the Day.`, `- /-otd remove [username] - Remove a user's nomination for the Thing of the Day and prevent them from voting again until the next round. Requires: % @ # &`, `- /-otd end - End nominations for the Thing of the Day and set it to a randomly selected nomination. Requires: % @ # &`, `- /-otd removewinner [nomination] - Remove a winner with the given [nomination] from the winners list. Requires: % @ # &`, `- /-otd force [nomination] - Forcibly sets the Thing of the Day without a nomination round. Requires: # &`, `- /-otd delay - Turns off the automatic 20 minute timer for Thing of the Day voting rounds. Requires: % @ # &`, `- /-otd set property: value[, property: value] - Set the winner, quote, song, link or image for the current Thing of the Day. Requires: % @ # &`, `- /-otd winners - Displays a list of previous things of the day.`, `- /-otd toggleupdate [on|off] - Changes the Thing of the Day to display on nomination ([on] to update, [off] to turn off updates). Requires: % @ # &` ]; for (const otd in otdData) { const data = otdData[otd]; const settings = data.settings; const room = Rooms.get(settings.roomid); if (!room) { Monitor.warn(`Room for -otd ${settings.title} of the ${settings.timeLabel} (${settings.roomid}) not found.`); continue; } OtdHandler.create(room, settings); } for (const [k, v] of otds) { pages[k] = function() { return v.generateWinnerList(this); }; commands[k] = otdCommands; commands[`${k}help`] = otdHelp; } const handlers = { onRenameRoom(oldID, newID, room) { for (const otd in otdData) { const data = otdData[otd]; if (data.settings.roomid === oldID) { data.settings.roomid = newID; const handler = otds.get(otd); handler.room = room; handler.save(); } } } }; const punishmentfilter = (user, punishment) => { user = toID(user); if (!["NAMELOCK", "BAN"].includes(punishment.type)) return; for (const handler of otds.values()) { handler.removeNomination(user); } }; //# sourceMappingURL=thing-of-the-day.js.map