Jofthomas's picture
Jofthomas HF staff
Upload 4781 files
5c2ed06 verified
{
"version": 3,
"sources": ["../../../server/chat-plugins/responder.ts"],
"sourcesContent": ["/**\n * PS Help room auto-response plugin.\n * Uses Regex to match room frequently asked question (RFAQ) entries,\n * and replies if a match is found.\n * Supports configuration, and works in all rooms, though intended mainly for Help.\n * Written by Mia.\n * @author mia-pi-git\n */\n\nimport { FS, Utils } from '../../lib';\nimport { LogViewer } from './chatlog';\nimport { roomFaqs, visualizeFaq } from './room-faqs';\n\nconst DATA_PATH = 'config/chat-plugins/responder.json';\nconst LOG_PATH = Monitor.logPath('responder.jsonl').path;\n\nexport let answererData: { [roomid: string]: PluginData } = {};\n\ntry {\n\tanswererData = JSON.parse(FS(DATA_PATH).readSync());\n} catch {}\n\n/**\n * A message caught by the filter.\n */\ninterface LoggedMessage {\n\t/** Message that's matched by the filter. */\n\tmessage: string;\n\t/** The FAQ that it's matched to. */\n\tfaqName: string;\n\t/** The regex that it's matched to. */\n\tregex: string;\n\tdate: string;\n}\ninterface PluginData {\n\t/** Word pairs that have been marked as a match for a specific FAQ. */\n\tpairs: { [k: string]: string[] };\n\t/** Common terms to be ignored in question parsing. */\n\tignore?: string[];\n}\n\nexport class AutoResponder {\n\tdata: PluginData;\n\troom: Room;\n\tconstructor(room: Room, data?: PluginData) {\n\t\tthis.room = room;\n\t\tthis.data = data || { pairs: {}, ignore: [] };\n\t\tAutoResponder.migrateStats(this.data, this);\n\t}\n\tstatic migrateStats(data: any, responder: AutoResponder) {\n\t\tif (!data.stats) return data;\n\t\tfor (const date in data.stats) {\n\t\t\tfor (const entry of data.stats[date].matches) {\n\t\t\t\tvoid this.logMessage(responder.room.roomid, { ...entry, date });\n\t\t\t}\n\t\t}\n\t\tdelete data.stats;\n\t\tresponder.data = data;\n\t\tresponder.writeState();\n\t\treturn data;\n\t}\n\tstatic logStream = FS(LOG_PATH).createAppendStream();\n\tstatic logMessage(roomid: RoomID, entry: LoggedMessage) {\n\t\treturn this.logStream.writeLine(JSON.stringify({\n\t\t\t...entry,\n\t\t\troom: roomid,\n\t\t\tregex: entry.regex.toString(),\n\t\t}));\n\t}\n\tfind(question: string, user?: User) {\n\t\t// sanity slice, APPARENTLY people are dumb.\n\t\tquestion = question.slice(0, 300);\n\t\tconst room = this.room;\n\t\tconst helpFaqs = roomFaqs[room.roomid];\n\t\tif (!helpFaqs) return null;\n\t\tconst normalized = Chat.normalize(question);\n\t\tif (this.data.ignore) {\n\t\t\tif (this.data.ignore.some(t => new RegExp(t, \"i\").test(normalized))) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tconst faqs = Object.keys(helpFaqs).filter(item => !helpFaqs[item].alias);\n\t\tfor (const faq of faqs) {\n\t\t\tconst match = this.test(normalized, faq);\n\t\t\tif (match) {\n\t\t\t\tif (user) {\n\t\t\t\t\tconst timestamp = Chat.toTimestamp(new Date()).split(' ')[1];\n\t\t\t\t\tconst log = `${timestamp} |c| ${user.name}|${question}`;\n\t\t\t\t\tthis.log(log, faq, match.regex);\n\t\t\t\t}\n\t\t\t\treturn helpFaqs[match.faq];\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\tvisualize(question: string, hideButton?: boolean, user?: User) {\n\t\tconst response = this.find(question, user);\n\t\tif (response) {\n\t\t\tlet buf = '';\n\t\t\tbuf += Utils.html`<strong>You said:</strong> ${question}<br />`;\n\t\t\tbuf += `<strong>Our automated reply:</strong> ${Chat.collapseLineBreaksHTML(visualizeFaq(response))}`;\n\t\t\tif (!hideButton) {\n\t\t\t\tbuf += Utils.html`<hr /><button class=\"button\" name=\"send\" value=\"A: ${question}\">`;\n\t\t\t\tbuf += `Send to ${this.room.title} if you weren't answered correctly. </button>`;\n\t\t\t}\n\t\t\treturn buf;\n\t\t}\n\t\treturn null;\n\t}\n\tgetFaqID(faq?: string) {\n\t\tif (!faq) throw new Chat.ErrorMessage(`Your input must be in the format [input] => [faq].`);\n\t\tfaq = faq.trim();\n\t\tif (!faq) throw new Chat.ErrorMessage(`Your FAQ ID can't be empty.`);\n\t\tconst room = this.room;\n\t\tconst entry = roomFaqs[room.roomid][faq];\n\t\tif (!entry) throw new Chat.ErrorMessage(`FAQ ID \"${faq}\" not found.`);\n\n\t\tif (!entry.alias) return faq; // not an alias\n\t\treturn entry.source;\n\t}\n\tasync getStatsFor(date: string) {\n\t\tconst stream = FS(LOG_PATH).createReadStream();\n\t\tconst buf: LoggedMessage[] = [];\n\t\tfor await (const raw of stream.byLine()) {\n\t\t\ttry {\n\t\t\t\tconst data = JSON.parse(raw);\n\t\t\t\tif (data.date !== date || data.room !== this.room.roomid) continue;\n\t\t\t\tbuf.push(data);\n\t\t\t} catch {}\n\t\t}\n\t\treturn buf;\n\t}\n\n\tasync listDays() {\n\t\tconst stream = FS(LOG_PATH).createReadStream();\n\t\tconst buf = new Utils.Multiset<string>();\n\t\tfor await (const raw of stream.byLine()) {\n\t\t\ttry {\n\t\t\t\tconst data = JSON.parse(raw);\n\t\t\t\tif (!data.date || data.room !== this.room.roomid) continue;\n\t\t\t\tbuf.add(data.date);\n\t\t\t} catch {}\n\t\t}\n\t\treturn buf;\n\t}\n\n\t/**\n\t * Checks if the FAQ exists. If not, deletes all references to it.\n\t */\n\tupdateFaqData(faq: string) {\n\t\t// testing purposes\n\t\tif (Config.nofswriting) return true;\n\t\tconst room = this.room;\n\t\tif (!room) return;\n\t\tif (roomFaqs[room.roomid][faq]) return true;\n\t\tif (this.data.pairs[faq]) delete this.data.pairs[faq];\n\t\treturn false;\n\t}\n\tstringRegex(str: string, raw?: boolean) {\n\t\t[str] = Utils.splitFirst(str, '=>');\n\t\tconst args = str.split(',').map(item => item.trim());\n\t\tif (!raw && args.length > 10) {\n\t\t\tthrow new Chat.ErrorMessage(`Too many arguments.`);\n\t\t}\n\t\tif (str.length > 300 && !raw) throw new Chat.ErrorMessage(\"Your given string is too long.\");\n\t\treturn args.map(item => {\n\t\t\tconst split = item.split('&').map(string => {\n\t\t\t\t// allow raw regex for admins and users with @ in Dev\n\t\t\t\tif (raw) return string;\n\t\t\t\t// escape\n\t\t\t\treturn string.replace(/[\\\\^$.*+?()[\\]{}]/g, '\\\\$&').trim();\n\t\t\t});\n\t\t\treturn split.map(term => {\n\t\t\t\tif (term.length > 100 && !raw) {\n\t\t\t\t\tthrow new Chat.ErrorMessage(`One or more of your arguments is too long. Use less than 100 characters.`);\n\t\t\t\t}\n\t\t\t\tif (item.startsWith('|') || item.endsWith('|')) {\n\t\t\t\t\tthrow new Chat.ErrorMessage(`Invalid use of |. Make sure you have an option on either side.`);\n\t\t\t\t}\n\t\t\t\tif (term.startsWith('!')) {\n\t\t\t\t\treturn `^(?!.*${term.slice(1)})`;\n\t\t\t\t}\n\t\t\t\tif (!term.trim()) return null;\n\t\t\t\treturn `(?=.*?(${term.trim()}))`;\n\t\t\t}).filter(Boolean).join('');\n\t\t}).filter(Boolean).join('');\n\t}\n\ttest(question: string, faq: string) {\n\t\tif (!this.data.pairs[faq]) this.data.pairs[faq] = [];\n\t\tconst regexes = this.data.pairs[faq].map(item => new RegExp(item, \"i\"));\n\t\tif (!regexes.length) return;\n\t\tfor (const regex of regexes) {\n\t\t\tif (regex.test(question)) return { faq, regex: regex.toString() };\n\t\t}\n\t\treturn null;\n\t}\n\tlog(entry: string, faq: string, expression: string) {\n\t\tconst [day] = Utils.splitFirst(Chat.toTimestamp(new Date), ' ');\n\t\tvoid AutoResponder.logMessage(this.room.roomid, {\n\t\t\tmessage: entry,\n\t\t\tfaqName: faq,\n\t\t\tregex: expression,\n\t\t\tdate: day,\n\t\t});\n\t}\n\twriteState() {\n\t\tfor (const faq in this.data.pairs) {\n\t\t\t// while writing, clear old data. In the meantime, the rest of the data is inaccessible\n\t\t\t// so this is the best place to clear the data\n\t\t\tthis.updateFaqData(faq);\n\t\t}\n\t\tanswererData[this.room.roomid] = this.data;\n\t\treturn FS(DATA_PATH).writeUpdate(() => JSON.stringify(answererData));\n\t}\n\ttryAddRegex(inputString: string, raw?: boolean) {\n\t\tlet [args, faq] = inputString.split('=>').map(item => item.trim()) as [string, string | undefined];\n\t\tfaq = this.getFaqID(toID(faq));\n\t\tif (!this.data.pairs) this.data.pairs = {};\n\t\tif (!this.data.pairs[faq]) this.data.pairs[faq] = [];\n\t\tconst regex = raw ? args.trim() : this.stringRegex(args, raw);\n\t\tif (this.data.pairs[faq].includes(regex)) {\n\t\t\tthrow new Chat.ErrorMessage(`That regex is already stored.`);\n\t\t}\n\t\tChat.validateRegex(regex);\n\t\tthis.data.pairs[faq].push(regex);\n\t\treturn this.writeState();\n\t}\n\ttryRemoveRegex(faq: string, index: number) {\n\t\tfaq = this.getFaqID(faq);\n\t\tif (!this.data.pairs) this.data.pairs = {};\n\t\tif (!this.data.pairs[faq]) throw new Chat.ErrorMessage(`There are no regexes for ${faq}.`);\n\t\tif (!this.data.pairs[faq][index]) throw new Chat.ErrorMessage(\"Your provided index is invalid.\");\n\t\tthis.data.pairs[faq].splice(index, 1);\n\t\tthis.writeState();\n\t\treturn true;\n\t}\n\tstatic canOverride(user: User, room: Room) {\n\t\tconst devAuth = Rooms.get('development')?.auth;\n\t\treturn (devAuth?.atLeast(user, '%') && devAuth?.has(user.id) && room.auth.atLeast(user, '@')) || user.can('rangeban');\n\t}\n\tdestroy() {\n\t\tthis.writeState();\n\t\tthis.room.responder = null;\n\t\tthis.room = null!;\n\t}\n\tignore(terms: string[], context: Chat.CommandContext) {\n\t\tconst filtered = terms.map(t => context.filter(t)).filter(Boolean);\n\t\tif (filtered.length !== terms.length) {\n\t\t\tthrow new Chat.ErrorMessage(`Invalid terms.`);\n\t\t}\n\t\tif (terms.some(t => t.length > 300)) {\n\t\t\tthrow new Chat.ErrorMessage(`One of your terms is too long.`);\n\t\t}\n\t\tif (!this.data.ignore) this.data.ignore = [];\n\t\tthis.data.ignore.push(...terms);\n\t\tthis.writeState();\n\t\treturn terms;\n\t}\n\tunignore(terms: string[]) {\n\t\tif (!this.data.ignore) {\n\t\t\tthrow new Chat.ErrorMessage(`The autoresponse filter in this room has no ignored terms.`);\n\t\t}\n\t\tthis.data.ignore = this.data.ignore.filter(item => !terms.includes(item));\n\t\tthis.writeState();\n\t\treturn true;\n\t}\n}\n\n// update all responders\nfor (const room of Rooms.rooms.values()) {\n\troom.responder?.destroy();\n\tif (answererData[room.roomid]) {\n\t\troom.responder = new AutoResponder(room, answererData[room.roomid]);\n\t}\n}\n\nconst BYPASS_TERMS = ['a:', 'A:', '!', '/'];\n\nexport const chatfilter: Chat.ChatFilter = function (message, user, room) {\n\tif (BYPASS_TERMS.some(t => message.startsWith(t))) {\n\t\t// do not return `message` or it will bypass all filters\n\t\t// including super important filters like against `/html`\n\t\treturn;\n\t}\n\tif (room?.responder && room.auth.get(user.id) === ' ') {\n\t\tconst responder = room.responder;\n\t\tconst reply = responder.visualize(message, false, user);\n\t\tif (!reply) {\n\t\t\treturn message;\n\t\t} else {\n\t\t\tthis.sendReply(`|uhtml|askhelp-${user}-${toID(message)}|<div class=\"infobox\">${reply}</div>`);\n\t\t\tconst trimmedMessage = `<div class=\"infobox\">${responder.visualize(message, true)}</div>`;\n\t\t\tsetTimeout(() => {\n\t\t\t\tthis.sendReply(`|uhtmlchange|askhelp-${user}-${toID(message)}|${trimmedMessage}`);\n\t\t\t}, 10 * 1000);\n\t\t\treturn false;\n\t\t}\n\t}\n};\n\nexport const commands: Chat.ChatCommands = {\n\tquestion(target, room, user) {\n\t\troom = this.requireRoom();\n\t\tconst responder = room.responder;\n\t\tif (!responder) return this.errorReply(`This room does not have an autoresponder configured.`);\n\t\tif (!target) return this.parse(\"/help question\");\n\t\tconst reply = responder.visualize(target, true);\n\t\tif (!reply) return this.sendReplyBox(`No answer found.`);\n\t\tthis.runBroadcast();\n\t\tthis.sendReplyBox(reply);\n\t},\n\tquestionhelp: [\"/question [question] - Asks the current room's auto-response filter a question.\"],\n\n\tar: 'autoresponder',\n\tautoresponder: {\n\t\t''(target, room) {\n\t\t\troom = this.requireRoom();\n\t\t\tconst responder = room.responder;\n\t\t\tif (!responder) {\n\t\t\t\treturn this.errorReply(`This room has not configured an autoresponder.`);\n\t\t\t}\n\t\t\tif (!target) {\n\t\t\t\treturn this.parse('/help autoresponder');\n\t\t\t}\n\t\t\treturn this.parse(`/j view-autoresponder-${room.roomid}-${target}`);\n\t\t},\n\t\tview(target, room, user) {\n\t\t\troom = this.requireRoom();\n\t\t\treturn this.parse(`/join view-autoresponder-${room.roomid}-${target}`);\n\t\t},\n\t\ttoggle(target, room, user) {\n\t\t\troom = this.requireRoom();\n\t\t\tif (!target) {\n\t\t\t\treturn this.sendReply(\n\t\t\t\t\t`The Help auto-response filter is currently set to: ${room.responder ? 'ON' : \"OFF\"}`\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.checkCan('ban', null, room);\n\t\t\tif (room.settings.isPrivate === true) {\n\t\t\t\treturn this.errorReply(`Secret rooms cannot enable an autoresponder.`);\n\t\t\t}\n\t\t\tif (this.meansYes(target)) {\n\t\t\t\tif (room.responder) return this.errorReply(`The Autoresponder for this room is already enabled.`);\n\t\t\t\troom.responder = new AutoResponder(room, answererData[room.roomid]);\n\t\t\t\troom.responder.writeState();\n\t\t\t}\n\t\t\tif (this.meansNo(target)) {\n\t\t\t\tif (!room.responder) return this.errorReply(`The Autoresponder for this room is already disabled.`);\n\t\t\t\troom.responder.destroy();\n\t\t\t}\n\t\t\tthis.privateModAction(`${user.name} ${!room.responder ? 'disabled' : 'enabled'} the auto-response filter.`);\n\t\t\tthis.modlog(`AUTOFILTER`, null, !room.responder ? 'OFF' : 'ON');\n\t\t},\n\t\tforceadd: 'add',\n\t\tadd(target, room, user, connection, cmd) {\n\t\t\troom = this.requireRoom();\n\t\t\tif (!room.responder) {\n\t\t\t\treturn this.errorReply(`This room has not configured an auto-response filter.`);\n\t\t\t}\n\t\t\tconst force = cmd === 'forceadd';\n\t\t\tif (force && !AutoResponder.canOverride(user, room)) {\n\t\t\t\treturn this.errorReply(`You cannot use raw regex - use /autoresponder add instead.`);\n\t\t\t}\n\t\t\tthis.checkCan('ban', null, room);\n\t\t\troom.responder.tryAddRegex(target, force);\n\t\t\tthis.privateModAction(`${user.name} added regex for \"${target.split('=>')[0]}\" to the autoresponder.`);\n\t\t\tthis.modlog(`AUTOFILTER ADD`, null, target);\n\t\t},\n\t\tremove(target, room, user) {\n\t\t\tconst [faq, index] = target.split(',');\n\t\t\troom = this.requireRoom();\n\t\t\tif (!room.responder) {\n\t\t\t\treturn this.errorReply(`${room.title} has not configured an auto-response filter.`);\n\t\t\t}\n\t\t\tthis.checkCan('ban', null, room);\n\t\t\tconst num = parseInt(index);\n\t\t\tif (isNaN(num)) return this.errorReply(\"Invalid index.\");\n\t\t\troom.responder.tryRemoveRegex(faq, num - 1);\n\t\t\tthis.privateModAction(`${user.name} removed regex ${num} from the usable regexes for ${faq}.`);\n\t\t\tthis.modlog('AUTOFILTER REMOVE', null, `removed regex ${index} for FAQ ${faq}`);\n\t\t\tconst pages = [`keys`, `pairs`];\n\t\t\tfor (const p of pages) {\n\t\t\t\tthis.refreshPage(`autofilter-${room.roomid}-${p}`);\n\t\t\t}\n\t\t},\n\t\tignore(target, room, user) {\n\t\t\troom = this.requireRoom();\n\t\t\tif (!room.responder) {\n\t\t\t\treturn this.errorReply(`This room has not configured an auto-response filter.`);\n\t\t\t}\n\t\t\tthis.checkCan('ban', null, room);\n\t\t\tif (!toID(target)) {\n\t\t\t\treturn this.parse(`/help autoresponder`);\n\t\t\t}\n\t\t\tconst targets = target.split(',');\n\t\t\troom.responder.ignore(targets, this);\n\t\t\tthis.privateModAction(\n\t\t\t\t`${user.name} added ${Chat.count(targets.length, \"terms\")} to the autoresponder ignore list.`\n\t\t\t);\n\t\t\tthis.modlog(`AUTOFILTER IGNORE`, null, target);\n\t\t},\n\t\tunignore(target, room, user) {\n\t\t\troom = this.requireRoom();\n\t\t\tif (!room.responder) {\n\t\t\t\treturn this.errorReply(`${room.title} has not configured an auto-response filter.`);\n\t\t\t}\n\t\t\tthis.checkCan('ban', null, room);\n\t\t\tif (!toID(target)) {\n\t\t\t\treturn this.parse(`/help autoresponder`);\n\t\t\t}\n\t\t\tconst targets = target.split(',');\n\t\t\troom.responder.unignore(targets);\n\t\t\tthis.privateModAction(`${user.name} removed ${Chat.count(targets.length, \"terms\")} from the autoresponder ignore list.`);\n\t\t\tthis.modlog(`AUTOFILTER UNIGNORE`, null, target);\n\t\t\tif (this.connection.openPages?.has(`autoresponder-${room.roomid}-ignore`)) {\n\t\t\t\treturn this.parse(`/join view-autoresponder-${room.roomid}-ignore`);\n\t\t\t}\n\t\t},\n\t},\n\tautoresponderhelp() {\n\t\tconst help = [\n\t\t\t`<code>/autoresponder view [page]</code> - Views the Autoresponder page [page]. (options: keys, stats)`,\n\t\t\t`<code>/autoresponder toggle [on | off]</code> - Enables or disables the Autoresponder for the current room. Requires: @ # ~`,\n\t\t\t`<code>/autoresponder add [input] => [faq]</code> - Adds regex made from the input string to the current room's Autoresponder, to respond with [faq] to matches.`,\n\t\t\t`<code>/autoresponder remove [faq], [regex index]</code> - removes the regex matching the [index] from the current room's responses for [faq].`,\n\t\t\t`Indexes can be found in /autoresponder keys.`,\n\t\t\t`Requires: @ # ~`,\n\t\t];\n\t\treturn this.sendReplyBox(help.join('<br/ >'));\n\t},\n};\n\nexport const pages: Chat.PageTable = {\n\tasync autoresponder(args, user) {\n\t\tconst room = this.requireRoom();\n\t\tif (!room.responder) {\n\t\t\treturn this.errorReply(`${room.title} does not have a configured autoresponder.`);\n\t\t}\n\t\targs.shift();\n\t\tconst roomData = answererData[room.roomid];\n\t\tconst canChange = user.can('ban', null, room);\n\t\tlet buf = '';\n\t\tconst refresh = (type: string, extra?: string[]) => {\n\t\t\tif (extra) extra = extra.filter(Boolean);\n\t\t\tlet button = `<button class=\"button\" name=\"send\" value=\"/join view-autoresponder-${room.roomid}-${type}`;\n\t\t\tbutton += `${extra?.length ? `-${extra.join('-')}` : ''}\" style=\"float: right\">`;\n\t\t\tbutton += `<i class=\"fa fa-refresh\"></i> Refresh</button><br />`;\n\t\t\treturn button;\n\t\t};\n\t\tconst back = `<br /><a roomid=\"view-autoresponder-${room.roomid}\">Back to all</a>`;\n\t\tswitch (args[0]) {\n\t\tcase 'stats':\n\t\t\targs.shift();\n\t\t\tthis.checkCan('mute', null, room);\n\t\t\tconst date = args.join('-') || '';\n\t\t\tif (!!date && isNaN(new Date(date).getTime())) {\n\t\t\t\treturn `<h2>Invalid date.</h2>`;\n\t\t\t}\n\t\t\tbuf = `<div class=\"pad\"><strong>Stats for the ${room.title} auto-response filter${date ? ` on ${date}` : ''}.</strong>`;\n\t\t\tbuf += `${back}${refresh('stats', [date])}<hr />`;\n\t\t\tif (date) {\n\t\t\t\tconst stats = await room.responder.getStatsFor(date);\n\t\t\t\tif (!stats) return `<h2>No stats.</h2>`;\n\t\t\t\tthis.title = `[Autoresponder Stats] ${date ? date : ''}`;\n\t\t\t\tif (!stats.length) return `<h2>No stats for ${date}.</h2>`;\n\t\t\t\tbuf += `<strong>Total messages answered: ${stats.length}</strong><hr />`;\n\t\t\t\tbuf += `<details><summary>All messages and the corresponding answers (FAQs):</summary>`;\n\t\t\t\tfor (const entry of stats) {\n\t\t\t\t\tbuf += `<small>Message:</small>${LogViewer.renderLine(entry.message)}`;\n\t\t\t\t\tbuf += `<small>FAQ: ${entry.faqName}</small><br />`;\n\t\t\t\t\tbuf += `<small>Regex: <code>${entry.regex}</code></small> <hr />`;\n\t\t\t\t}\n\t\t\t\treturn LogViewer.linkify(buf);\n\t\t\t}\n\t\t\tbuf += `<strong> No date specified.<br />`;\n\t\t\tconst days: string[] = [];\n\t\t\tlet totalCount = 0;\n\t\t\tconst dayKeys = await room.responder.listDays();\n\t\t\tfor (const [dateKey, total] of dayKeys) {\n\t\t\t\ttotalCount += total;\n\t\t\t\tdays.push(`- <a roomid=\"view-autoresponder-${room.roomid}-stats-${dateKey}\">${dateKey}</a> (${total})`);\n\t\t\t}\n\t\t\tbuf += `Dates with stats:</strong><small>(total matches: ${totalCount})</small><br /><br />`;\n\t\t\tbuf += days.join('<br />');\n\t\t\tbreak;\n\t\tcase 'pairs':\n\t\tcase 'keys':\n\t\t\tthis.title = '[Autoresponder Regexes]';\n\t\t\tthis.checkCan('show', null, room);\n\t\t\tbuf = `<div class=\"pad\"><h2>${room.title} responder regexes and responses:</h2>${back}${refresh('keys')}<hr />`;\n\t\t\tbuf += Object.entries(roomData.pairs).map(([item, regexes]) => {\n\t\t\t\tif (regexes.length < 1) return null;\n\t\t\t\tlet buffer = `<details><summary>${item}</summary>`;\n\t\t\t\tbuffer += `<div class=\"ladder pad\"><table><tr><th>Index</th><th>Regex</th>`;\n\t\t\t\tif (canChange) buffer += `<th>Options</th>`;\n\t\t\t\tbuffer += `</tr>`;\n\t\t\t\tfor (const regex of regexes) {\n\t\t\t\t\tconst index = regexes.indexOf(regex) + 1;\n\t\t\t\t\tconst button = `<button class=\"button\" name=\"send\"value=\"/msgroom ${room.roomid},/ar remove ${item}, ${index}\">Remove</button>`;\n\t\t\t\t\tbuffer += `<tr><td>${index}</td><td><code>${regex}</code></td>`;\n\t\t\t\t\tif (canChange) buffer += `<td>${button}</td></tr>`;\n\t\t\t\t}\n\t\t\t\tbuffer += `</details>`;\n\t\t\t\treturn buffer;\n\t\t\t}).filter(Boolean).join('<hr />');\n\t\t\tbreak;\n\t\tcase 'ignore':\n\t\t\tthis.title = `[${room.title} Autoresponder ignore list]`;\n\t\t\tbuf = `<div class=\"pad\"><h2>${room.title} responder terms to ignore:</h2>${back}${refresh('ignore')}<hr />`;\n\t\t\tif (!roomData.ignore) {\n\t\t\t\treturn this.errorReply(`No terms on ignore list.`);\n\t\t\t}\n\t\t\tfor (const term of roomData.ignore) {\n\t\t\t\tbuf += `- ${term} <button class=\"button\" name=\"send\"value=\"/msgroom ${room.roomid},/ar unignore ${term}\">Remove</button><br />`;\n\t\t\t}\n\t\t\tbuf += `</div>`;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthis.title = `[${room.title} Autoresponder]`;\n\t\t\tbuf = `<div class=\"pad\"><h2>Specify a filter page to view.</h2>`;\n\t\t\tbuf += `<hr /><strong>Options:</strong><hr />`;\n\t\t\tbuf += `<a roomid=\"view-autoresponder-${room.roomid}-stats\">Stats</a><hr />`;\n\t\t\tbuf += `<a roomid=\"view-autoresponder-${room.roomid}-keys\">Regex keys</a><hr/>`;\n\t\t\tbuf += `<a roomid=\"view-autoresponder-${room.roomid}-ignore\">Ignore list</a><hr/>`;\n\t\t\tbuf += `</div>`;\n\t\t}\n\t\treturn LogViewer.linkify(buf);\n\t},\n};\n\nexport const handlers: Chat.Handlers = {\n\tonRenameRoom(oldID, newID) {\n\t\tif (answererData[oldID]) {\n\t\t\tif (!answererData[newID]) answererData[newID] = { pairs: {} };\n\t\t\tObject.assign(answererData[newID], answererData[oldID]);\n\t\t\tdelete answererData[oldID];\n\t\t\tFS(DATA_PATH).writeUpdate(() => JSON.stringify(answererData));\n\t\t}\n\t},\n};\n"],
"mappings": "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",
"names": ["pages"]
}