From 2ea84725e2bf60fc484f3f67a68ea7a90d7cdf6c Mon Sep 17 00:00:00 2001 From: Kris Johnson <11083252+KrisXV@users.noreply.github.com> Date: Tue, 18 Feb 2020 14:26:44 -0700 Subject: [PATCH] Typescript Daily Spotlight, RoomFAQs, and Wi-Fi (#6373) --- server/chat-plugins/chat-monitor.ts | 61 +- ...{daily-spotlight.js => daily-spotlight.ts} | 57 +- .../{room-faqs.js => room-faqs.ts} | 39 +- server/chat-plugins/{wifi.js => wifi.ts} | 537 +++++++----------- server/rooms.ts | 5 + tsconfig.json | 4 - 6 files changed, 313 insertions(+), 390 deletions(-) rename server/chat-plugins/{daily-spotlight.js => daily-spotlight.ts} (81%) rename server/chat-plugins/{room-faqs.js => room-faqs.ts} (86%) rename server/chat-plugins/{wifi.js => wifi.ts} (72%) diff --git a/server/chat-plugins/chat-monitor.ts b/server/chat-plugins/chat-monitor.ts index 8a3fea5c52..40142bd6f2 100644 --- a/server/chat-plugins/chat-monitor.ts +++ b/server/chat-plugins/chat-monitor.ts @@ -33,16 +33,22 @@ const EVASION_DETECTION_SUBSTITUTIONS: {[k: string]: string[]} = { e: ["e", "3", "é", "ê", "E", "ⓔ", "Ⓔ", "є", "͏", "Ɇ", "ệ", "Ệ", "Ꮛ", "ε", "Σ", "ḕ", "Ḕ", "Ꭼ", "ɛ", "̾", "e", "E", "ᴇ", "ǝ", "🅔", "𝐞", "𝐄", "𝘦", "𝘌", "𝙚", "𝙀", "ℯ", "𝓮", "𝓔", "𝕖", "𝔻", "𝔢", "𝔇", "𝖊", "𝕰", "🄴", "🅴", "𝑒", "𝐸", "ҽ", "𝚎", "𝙴", "€", "е"], f: ["f", "ᖴ", "F", "ⓕ", "Ⓕ", "₣", "ḟ", "Ḟ", "Ꭶ", "ғ", "ʄ", "f", "F", "ɟ", "🅕", "𝐟", "𝐅", "𝘧", "𝘍", "𝙛", "𝙁", "𝒻", "𝓯", "𝓕", "𝕗", "𝔼", "𝔣", "𝔈", "𝖋", "𝕱", "🄵", "🅵", "𝐹", "ϝ", "𝚏", "𝙵", "Ϝ", "f"], g: ["g", "q", "6", "9", "G", "ⓖ", "Ⓖ", "͏", "₲", "ġ", "Ġ", "Ꮆ", "ϑ", "Ḡ", "ɢ", "̾", "g", "G", "ƃ", "🅖", "𝐠", "𝐆", "𝘨", "𝘎", "𝙜", "𝙂", "ℊ", "𝓰", "𝓖", "𝕘", "𝔽", "𝔤", "𝔉", "𝖌", "𝕲", "🄶", "🅶", "𝑔", "𝒢", "ɠ", "𝚐", "𝙶", "❡", "ց"], - h: ["h", "ᕼ", "H", "ⓗ", "Ⓗ", "н", "Ⱨ", "ḧ", "Ḧ", "Ꮒ", "ɦ", "h", "H", "ʜ", "ɥ", "🅗", "𝐡", "𝐇", "𝘩", "𝘏", "𝙝", "𝙃", "𝒽", "𝓱", "𝓗", "𝕙", "𝔾", "𝔥", "𝔊", "𝖍", "𝕳", "🄷", "🅷", "𝐻", "ԋ", "𝚑", "𝙷", "♄", "h"], + h: [ + "h", "ᕼ", "H", "ⓗ", "Ⓗ", "н", "Ⱨ", "ḧ", "Ḧ", "Ꮒ", "ɦ", "h", "H", "ʜ", "ɥ", "🅗", "𝐡", "𝐇", "𝘩", "𝘏", "𝙝", "𝙃", "𝒽", "𝓱", "𝓗", "𝕙", "𝔾", "𝔥", "𝔊", "𝖍", "𝕳", "🄷", "🅷", "𝐻", "ԋ", "𝚑", "𝙷", "♄", "h", + ], i: ["i", "!", "l", "1", "í", "I", "ⓘ", "Ⓘ", "ι", "͏", "ł", "ï", "Ï", "Ꭵ", "ḭ", "Ḭ", "ɨ", "̾", "i", "I", "ɪ", "ı", "🅘", "𝐢", "𝐈", "𝘪", "𝘐", "𝙞", "𝙄", "𝒾", "𝓲", "𝓘", "𝕚", "ℍ", "𝔦", "ℌ", "𝖎", "𝕴", "🄸", "🅸", "𝐼", "𝚒", "𝙸", "♗", "і", "¡", "|"], j: ["j", "ᒍ", "J", "ⓙ", "Ⓙ", "נ", "Ꮰ", "ϳ", "ʝ", "j", "J", "ᴊ", "ɾ", "🅙", "𝐣", "𝐉", "𝘫", "𝘑", "𝙟", "𝙅", "𝒿", "𝓳", "𝓙", "𝕛", "", "𝔧", "𝖏", "𝕵", "🄹", "🅹", "𝒥", "𝚓", "𝙹", "♪", "ј"], k: ["k", "K", "ⓚ", "Ⓚ", "к", "͏", "₭", "ḳ", "Ḳ", "Ꮶ", "κ", "Ƙ", "ӄ", "̾", "k", "K", "ᴋ", "ʞ", "🅚", "𝐤", "𝐊", "𝘬", "𝘒", "𝙠", "𝙆", "𝓀", "𝓴", "𝓚", "𝕜", "𝕀", "𝔨", "ℑ", "𝖐", "𝕶", "🄺", "🅺", "𝒦", "ƙ", "𝚔", "𝙺", "ϰ", "k"], l: ["l", "i", "1", "/", "|", "ᒪ", "L", "ⓛ", "Ⓛ", "ℓ", "Ⱡ", "ŀ", "Ŀ", "Ꮭ", "Ḷ", "Ꮮ", "ʟ", "l", "L", "🅛", "𝐥", "𝐋", "𝘭", "𝘓", "𝙡", "𝙇", "𝓁", "𝓵", "𝓛", "𝕝", "𝕁", "𝔩", "", "𝖑", "𝕷", "🄻", "🅻", "𝐿", "ʅ", "𝚕", "𝙻", "↳", "ⅼ"], - m: ["m", "ᗰ", "M", "ⓜ", "Ⓜ", "м", "͏", "₥", "ṃ", "Ṃ", "Ꮇ", "ϻ", "Μ", "ṁ", "Ṁ", "ʍ", "̾", "m", "M", "ᴍ", "ɯ", "🅜", "𝐦", "𝐌", "𝘮", "𝘔", "𝙢", "𝙈", "𝓂", "𝓶", "𝓜", "𝕞", "𝕂", "𝔪", "𝔍", "𝖒", "𝕸", "🄼", "🅼", "𝑀", "ɱ", "𝚖", "𝙼", "♔", "ⅿ"], + m: [ + "m", "ᗰ", "M", "ⓜ", "Ⓜ", "м", "͏", "₥", "ṃ", "Ṃ", "Ꮇ", "ϻ", "Μ", "ṁ", "Ṁ", "ʍ", "̾", "m", "M", "ᴍ", "ɯ", "🅜", "𝐦", "𝐌", "𝘮", "𝘔", "𝙢", "𝙈", "𝓂", "𝓶", "𝓜", "𝕞", "𝕂", "𝔪", "𝔍", "𝖒", "𝕸", "🄼", "🅼", "𝑀", "ɱ", "𝚖", "𝙼", "♔", "ⅿ", + ], n: ["n", "ñ", "ᑎ", "N", "ⓝ", "Ⓝ", "и", "₦", "ń", "Ń", "Ꮑ", "π", "∏", "Ṇ", "ռ", "n", "N", "ɴ", "🅝", "𝐧", "𝐍", "𝘯", "𝘕", "𝙣", "𝙉", "𝓃", "𝓷", "𝓝", "𝕟", "𝕃", "𝔫", "𝔎", "𝖓", "𝕹", "🄽", "🅽", "𝒩", "ɳ", "𝚗", "𝙽", "♫", "ո"], o: ["o", "0", "ó", "ô", "õ", "ú", "O", "ⓞ", "Ⓞ", "σ", "͏", "Ø", "ö", "Ö", "Ꭷ", "Θ", "ṏ", "Ṏ", "Ꮎ", "օ", "̾", "o", "O", "ᴏ", "🅞", "𝐨", "𝐎", "𝘰", "𝘖", "𝙤", "𝙊", "ℴ", "𝓸", "𝓞", "𝕠", "𝕄", "𝔬", "𝔏", "𝖔", "𝕺", "🄾", "🅾", "𝑜", "𝒪", "𝚘", "𝙾", "⊙", "ο"], p: ["p", "ᑭ", "P", "ⓟ", "Ⓟ", "ρ", "₱", "ṗ", "Ṗ", "Ꭾ", "Ƥ", "Ꮲ", "ք", "p", "P", "ᴘ", "🅟", "𝐩", "𝐏", "𝘱", "𝘗", "𝙥", "𝙋", "𝓅", "𝓹", "𝓟", "𝕡", "ℕ", "𝔭", "𝔐", "𝖕", "𝕻", "🄿", "🅿", "𝒫", "𝚙", "𝙿", "р"], - q: ["q", "ᑫ", "Q", "ⓠ", "Ⓠ", "͏", "Ꭴ", "φ", "Ⴓ", "զ", "̾", "q", "Q", "ϙ", "ǫ", "🅠", "𝐪", "𝐐", "𝘲", "𝘘", "𝙦", "𝙌", "𝓆", "𝓺", "𝓠", "𝕢", "", "𝔮", "𝔑", "𝖖", "𝕼", "🅀", "🆀", "𝒬", "𝚚", "𝚀", "☭", "ԛ"], + q: [ + "q", "ᑫ", "Q", "ⓠ", "Ⓠ", "͏", "Ꭴ", "φ", "Ⴓ", "զ", "̾", "q", "Q", "ϙ", "ǫ", "🅠", "𝐪", "𝐐", "𝘲", "𝘘", "𝙦", "𝙌", "𝓆", "𝓺", "𝓠", "𝕢", "", "𝔮", "𝔑", "𝖖", "𝕼", "🅀", "🆀", "𝒬", "𝚚", "𝚀", "☭", "ԛ", + ], r: ["r", "ᖇ", "R", "ⓡ", "Ⓡ", "я", "Ɽ", "ŕ", "Ŕ", "Ꮢ", "г", "Γ", "ṙ", "Ṙ", "ʀ", "r", "R", "ɹ", "🅡", "𝐫", "𝐑", "𝘳", "𝘙", "𝙧", "𝙍", "𝓇", "𝓻", "𝓡", "𝕣", "𝕆", "𝔯", "𝔒", "𝖗", "𝕽", "🅁", "🆁", "𝑅", "ɾ", "𝚛", "𝚁", "☈", "r"], s: ["s", "5", "ᔕ", "S", "ⓢ", "Ⓢ", "ѕ", "͏", "₴", "ṩ", "Ṩ", "Ꮥ", "Ѕ", "Ṡ", "ֆ", "̾", "s", "S", "ꜱ", "🅢", "𝐬", "𝐒", "𝘴", "𝘚", "𝙨", "𝙎", "𝓈", "𝓼", "𝓢", "𝕤", "ℙ", "𝔰", "𝔓", "𝖘", "𝕾", "🅂", "🆂", "𝒮", "ʂ", "𝚜", "𝚂", "ѕ"], t: ["t", "+", "T", "ⓣ", "Ⓣ", "т", "₮", "ẗ", "Ṯ", "Ꮦ", "τ", "Ƭ", "Ꮖ", "ȶ", "t", "T", "ᴛ", "ʇ", "🅣", "𝐭", "𝐓", "𝘵", "𝘛", "𝙩", "𝙏", "𝓉", "𝓽", "𝓣", "𝕥", "", "𝔱", "𝔔", "𝖙", "𝕿", "🅃", "🆃", "𝒯", "ƚ", "𝚝", "𝚃", "☂", "t"], @@ -50,7 +56,9 @@ const EVASION_DETECTION_SUBSTITUTIONS: {[k: string]: string[]} = { v: ["v", "ᐯ", "V", "ⓥ", "Ⓥ", "ν", "ṿ", "Ṿ", "Ꮙ", "Ʋ", "Ṽ", "ʋ", "v", "V", "ᴠ", "ʌ", "🅥", "𝐯", "𝐕", "𝘷", "𝘝", "𝙫", "𝙑", "𝓋", "𝓿", "𝓥", "𝕧", "", "𝔳", "𝖛", "𝖁", "🅅", "🆅", "𝒱", "𝚟", "𝚅", "✓", "ⅴ"], w: ["w", "ᗯ", "W", "ⓦ", "Ⓦ", "ω", "͏", "₩", "ẅ", "Ẅ", "Ꮗ", "ш", "Ш", "ẇ", "Ẇ", "Ꮃ", "ա", "̾", "w", "W", "ᴡ", "ʍ", "🅦", "𝐰", "𝐖", "𝘸", "𝘞", "𝙬", "𝙒", "𝓌", "𝔀", "𝓦", "𝕨", "ℝ", "𝔴", "𝔖", "𝖜", "𝖂", "🅆", "🆆", "𝒲", "ɯ", "𝚠", "𝚆", "ԝ"], x: ["x", "᙭", "X", "ⓧ", "Ⓧ", "χ", "Ӿ", "ẍ", "Ẍ", "ጀ", "ϰ", "Ж", "х", "Ӽ", "x", "X", "🅧", "𝐱", "𝐗", "𝘹", "𝘟", "𝙭", "𝙓", "𝓍", "𝔁", "𝓧", "𝕩", "", "𝔵", "𝔗", "𝖝", "𝖃", "🅇", "🆇", "𝒳", "𝚡", "𝚇", "⌘", "х"], - y: ["y", "Y", "ⓨ", "Ⓨ", "у", "͏", "Ɏ", "ÿ", "Ÿ", "Ꭹ", "ψ", "Ψ", "ẏ", "Ẏ", "Ꮍ", "ч", "ʏ", "̾", "y", "Y", "ʎ", "🅨", "𝐲", "𝐘", "𝘺", "𝘠", "𝙮", "𝙔", "𝓎", "𝔂", "𝓨", "𝕪", "𝕊", "𝔶", "𝔘", "𝖞", "𝖄", "🅈", "🆈", "𝒴", "ყ", "𝚢", "𝚈", "☿", "у"], + y: [ + "y", "Y", "ⓨ", "Ⓨ", "у", "͏", "Ɏ", "ÿ", "Ÿ", "Ꭹ", "ψ", "Ψ", "ẏ", "Ẏ", "Ꮍ", "ч", "ʏ", "̾", "y", "Y", "ʎ", "🅨", "𝐲", "𝐘", "𝘺", "𝘠", "𝙮", "𝙔", "𝓎", "𝔂", "𝓨", "𝕪", "𝕊", "𝔶", "𝔘", "𝖞", "𝖄", "🅈", "🆈", "𝒴", "ყ", "𝚢", "𝚈", "☿", "у", + ], z: ["z", "ᘔ", "Z", "ⓩ", "Ⓩ", "Ⱬ", "ẓ", "Ẓ", "ፚ", "Ꮓ", "ʐ", "z", "Z", "ᴢ", "🅩", "𝐳", "𝐙", "𝘻", "𝘡", "𝙯", "𝙕", "𝓏", "𝔃", "𝓩", "𝕫", "𝕋", "𝔷", "𝔙", "𝖟", "𝖅", "🅉", "🆉", "𝒵", "ȥ", "𝚣", "𝚉", "☡", "z"], }; @@ -77,7 +85,9 @@ function saveFilters(force = false) { FS(MONITOR_FILE).writeUpdate(() => { let buf = 'Location\tWord\tPunishment\tReason\tTimes\r\n'; for (const key in Chat.monitors) { - buf += filterWords[key].map(word => renderEntry(Chat.monitors[key].location, word, Chat.monitors[key].punishment)).join(''); + buf += filterWords[key].map( + word => renderEntry(Chat.monitors[key].location, word, Chat.monitors[key].punishment) + ).join(''); } return buf; }, {throttle: force ? 0 : WRITE_THROTTLE_TIME}); @@ -274,7 +284,14 @@ void FS(MONITOR_FILE).readIfExists().then(data => { export const chatfilter: ChatFilter = function (message, user, room) { // eslint-disable-next-line no-misleading-character-class - let lcMessage = message.replace(/\u039d/g, 'N').toLowerCase().replace(/[\u200b\u007F\u00AD\uDB40\uDC00\uDC21]/g, '').replace(/\u03bf/g, 'o').replace(/\u043e/g, 'o').replace(/\u0430/g, 'a').replace(/\u0435/g, 'e').replace(/\u039d/g, 'e'); + let lcMessage = message + .replace(/\u039d/g, 'N').toLowerCase() + .replace(/[\u200b\u007F\u00AD\uDB40\uDC00\uDC21]/g, '') + .replace(/\u03bf/g, 'o') + .replace(/\u043e/g, 'o') + .replace(/\u0430/g, 'a') + .replace(/\u0435/g, 'e') + .replace(/\u039d/g, 'e'); lcMessage = lcMessage.replace(/__|\*\*|``|\[\[|\]\]/g, ''); const isStaffRoom = room && ((room.chatRoomData && room.roomid.endsWith('staff')) || room.roomid.startsWith('help-')); @@ -318,7 +335,14 @@ export const namefilter: NameFilter = (name, user) => { const id = toID(name); if (Chat.namefilterwhitelist.has(id)) return name; if (id === toID(user.trackRename)) return ''; - let lcName = name.replace(/\u039d/g, 'N').toLowerCase().replace(/[\u200b\u007F\u00AD]/g, '').replace(/\u03bf/g, 'o').replace(/\u043e/g, 'o').replace(/\u0430/g, 'a').replace(/\u0435/g, 'e').replace(/\u039d/g, 'e'); + let lcName = name + .replace(/\u039d/g, 'N').toLowerCase() + .replace(/[\u200b\u007F\u00AD]/g, '') + .replace(/\u03bf/g, 'o') + .replace(/\u043e/g, 'o') + .replace(/\u0430/g, 'a') + .replace(/\u0435/g, 'e') + .replace(/\u039d/g, 'e'); // Remove false positives. lcName = lcName.replace('herapist', '').replace('grape', '').replace('scrape', ''); @@ -355,7 +379,14 @@ export const loginfilter: LoginFilter = user => { } }; export const nicknamefilter: NameFilter = (name, user) => { - let lcName = name.replace(/\u039d/g, 'N').toLowerCase().replace(/[\u200b\u007F\u00AD]/g, '').replace(/\u03bf/g, 'o').replace(/\u043e/g, 'o').replace(/\u0430/g, 'a').replace(/\u0435/g, 'e').replace(/\u039d/g, 'e'); + let lcName = name + .replace(/\u039d/g, 'N').toLowerCase() + .replace(/[\u200b\u007F\u00AD]/g, '') + .replace(/\u03bf/g, 'o') + .replace(/\u043e/g, 'o') + .replace(/\u0430/g, 'a') + .replace(/\u0435/g, 'e') + .replace(/\u039d/g, 'e'); // Remove false positives. lcName = lcName.replace('herapist', '').replace('grape', '').replace('scrape', ''); @@ -378,7 +409,14 @@ export const nicknamefilter: NameFilter = (name, user) => { return name; }; export const statusfilter: StatusFilter = (status, user) => { - let lcStatus = status.replace(/\u039d/g, 'N').toLowerCase().replace(/[\u200b\u007F\u00AD]/g, '').replace(/\u03bf/g, 'o').replace(/\u043e/g, 'o').replace(/\u0430/g, 'a').replace(/\u0435/g, 'e').replace(/\u039d/g, 'e'); + let lcStatus = status + .replace(/\u039d/g, 'N').toLowerCase() + .replace(/[\u200b\u007F\u00AD]/g, '') + .replace(/\u03bf/g, 'o') + .replace(/\u043e/g, 'o') + .replace(/\u0430/g, 'a') + .replace(/\u0435/g, 'e') + .replace(/\u039d/g, 'e'); // Remove false positives. lcStatus = lcStatus.replace('herapist', '').replace('grape', '').replace('scrape', ''); // Check for blatant staff impersonation attempts. Ideally this could be completely generated from Config.grouplist @@ -473,7 +511,10 @@ export const commands: ChatCommands = { if (Chat.monitors[list].punishment === 'EVASION') { regex = constructEvasionRegex(word); } else { - regex = new RegExp(Chat.monitors[list].punishment === 'SHORTENER' ? `\\b${word}` : word, filterTo ? 'ig' : 'i'); // eslint-disable-line no-unused-vars + regex = new RegExp( + Chat.monitors[list].punishment === 'SHORTENER' ? `\\b${word}` : word, + filterTo ? 'ig' : 'i' + ); } } catch (e) { return this.errorReply(e.message.startsWith('Invalid regular expression: ') ? e.message : `Invalid regular expression: /${word}/: ${e.message}`); diff --git a/server/chat-plugins/daily-spotlight.js b/server/chat-plugins/daily-spotlight.ts similarity index 81% rename from server/chat-plugins/daily-spotlight.js rename to server/chat-plugins/daily-spotlight.ts index 20ab906997..5d68093efb 100644 --- a/server/chat-plugins/daily-spotlight.js +++ b/server/chat-plugins/daily-spotlight.ts @@ -1,15 +1,14 @@ 'use strict'; -/** @type {typeof import('../../lib/fs').FS} */ -const FS = require(/** @type {any} */('../../.lib-dist/fs')).FS; +import {FS} from '../../lib/fs'; +import * as fs from 'fs'; const DAY = 24 * 60 * 60 * 1000; const SPOTLIGHT_FILE = 'config/chat-plugins/spotlights.json'; -/** @type {{[k: string]: {[k: string]: {image: string?, description: string}[]}}} */ -let spotlights = {}; +let spotlights: {[k: string]: {[k: string]: {image?: string, description: string}[]}} = {}; try { - spotlights = require(`../../${SPOTLIGHT_FILE}`); + spotlights = JSON.parse(fs.readFileSync(`../../${SPOTLIGHT_FILE}`).toString()); } catch (e) { if (e.code !== 'MODULE_NOT_FOUND' && e.code !== 'ENOENT') throw e; } @@ -36,11 +35,7 @@ const midnight = new Date(); midnight.setHours(24, 0, 0, 0); let timeout = setTimeout(nextDaily, midnight.valueOf() - Date.now()); -/** - * @param {string?} image - * @param {string} description - */ -async function renderSpotlight(image, description) { +async function renderSpotlight(description: string, image?: string) { let imgHTML = ''; if (image) { @@ -51,12 +46,11 @@ async function renderSpotlight(image, description) { return `
| ${Chat.formatText(description, true).replace(/\n/g, ` `)} | ${imgHTML}
This room has no daily spotlights.
`; } else { - for (let key in spotlights[this.room.roomid]) { + for (const key in spotlights[this.room.roomid]) { buf += `${key}: | `;
for (const [i, spotlight] of spotlights[this.room.roomid][key].entries()) {
- const html = await renderSpotlight(spotlight.image, spotlight.description);
+ const html = await renderSpotlight(spotlight.description, spotlight.image);
buf += `|
|---|---|
| ${i ? i : 'Current'} | ${html} |
| ${this.sprite} Giver: ${this.giver} ${Chat.formatText(this.prize, true)}OT: ${Chat.escapeHTML(this.ot)}, TID: ${this.tid} | ` +
@@ -247,28 +211,26 @@ class Giveaway {
}
}
-class QuestionGiveaway extends Giveaway {
- /**
- * @param {User} host
- * @param {User} giver
- * @param {ChatRoom} room
- * @param {string} ot
- * @param {string} tid
- * @param {string} fc
- * @param {string} prize
- * @param {string} question
- * @param {string[]} answers
- */
- constructor(host, giver, room, ot, tid, fc, prize, question, answers) {
+export class QuestionGiveaway extends Giveaway {
+ type: string;
+ question: string;
+ answers: string[];
+ /** userid: number of guesses */
+ answered: {[userid: string]: number};
+ winner: User | null;
+
+ constructor(
+ host: User, giver: User, room: ChatRoom | GameRoom, ot: string, tid: string,
+ fc: string, prize: string, question: string, answers: string[]
+ ) {
super(host, giver, room, ot, tid, fc, prize);
this.type = 'question';
this.phase = 'pending';
this.question = question;
this.answers = QuestionGiveaway.sanitizeAnswers(answers);
- /** @type {{[k: string]: number}} */
- this.answered = {}; // userid: number of guesses
-
+ this.answered = {};
+ this.winner = null;
this.send(this.generateWindow('The question will be displayed in one minute! Use /ga to answer.'));
this.timer = setTimeout(() => this.start(), 1000 * 60);
@@ -285,11 +247,7 @@ class QuestionGiveaway extends Giveaway {
this.timer = setTimeout(() => this.end(false), 1000 * 60 * 5);
}
- /**
- * @param {User} user
- * @param {string} guess
- */
- guessAnswer(user, guess) {
+ guessAnswer(user: User, guess: string) {
if (this.phase !== 'started') return user.sendTo(this.room, "The giveaway has not started yet.");
if (this.checkJoined(user) && Object.values(this.joined).indexOf(user.id) < 0) return user.sendTo(this.room, "You have already joined the giveaway.");
@@ -299,10 +257,10 @@ class QuestionGiveaway extends Giveaway {
if (!this.answered[user.id]) this.answered[user.id] = 0;
if (this.answered[user.id] >= 3) return user.sendTo(this.room, "You have already guessed three times. You cannot guess anymore in this giveaway.");
- let sanitized = toID(guess);
+ const sanitized = toID(guess);
- for (let i = 0; i < this.answers.length; i++) {
- if (toID(this.answers[i]) === sanitized) {
+ for (const answer of this.answers.map(toID)) {
+ if (answer === sanitized) {
this.winner = user;
this.clearTimer();
return this.end(false);
@@ -312,34 +270,29 @@ class QuestionGiveaway extends Giveaway {
this.joined[user.latestIp] = user.id;
this.answered[user.id]++;
if (this.answered[user.id] >= 3) {
- user.sendTo(this.room, `Your guess '${guess}' is wrong. You have used up all of your guesses. Better luck next time!`);
+ user.sendTo(
+ this.room,
+ `Your guess '${guess}' is wrong. You have used up all of your guesses. Better luck next time!`
+ );
} else {
user.sendTo(this.room, `Your guess '${guess}' is wrong. Try again!`);
}
}
- /**
- * @param {string} key
- * @param {string} value
- * @param {User} user
- */
- change(key, value, user) {
+ change(key: string, value: string, user: User) {
if (user.id !== this.host.id) return user.sendTo(this.room, "Only the host can edit the giveaway.");
if (this.phase !== 'pending') return user.sendTo(this.room, "You cannot change the question or answer once the giveaway has started.");
if (key === 'question') {
this.question = value;
return user.sendTo(this.room, `The question has been changed to ${value}.`);
}
- let ans = QuestionGiveaway.sanitizeAnswers(value.split(',').map(val => val.trim()));
+ const ans = QuestionGiveaway.sanitizeAnswers(value.split(',').map(val => val.trim()));
if (!ans.length) return user.sendTo(this.room, "You must specify at least one answer and it must not contain any special characters.");
this.answers = ans;
user.sendTo(this.room, `The answer${Chat.plural(ans, "s have", "has")} been changed to ${ans.join(', ')}.`);
}
- /**
- * @param {boolean} force
- */
- end(force) {
+ end(force: boolean) {
if (force) {
this.clearTimer();
this.changeUhtml('
| ${rightSide} |
The GTS giveaway has finished.
`); this.room.modlog(`(wifi) GTS FINISHED: ${this.giver.name} has finished their GTS giveaway for "${this.summary}"`); this.send(`The GTS giveaway for a "${Chat.escapeHTML(this.lookfor)}" has finished.
`); - Giveaway.updateStats(this.monIds); + Giveaway.updateStats(this.monIDs); } - // @ts-ignore delete this.room.gtsga; return this.left; } // This currently doesn't match some of the edge cases the other pokemon matching function does account for (such as Type: Null). However, this should never be used as a fodder mon anyway, so I don't see a huge need to implement it. - /** - * @param {string} text - */ - static linkify(text) { - let parsed = text.toLowerCase().replace(/é/g, 'e'); + static linkify(text: string) { + const parsed = text.toLowerCase().replace(/é/g, 'e'); - for (let i in Dex.data.Pokedex) { + for (const i in Dex.data.Pokedex) { let id = i; if (!Dex.data.Pokedex[i].baseSpecies && (Dex.data.Pokedex[i].species.includes(' '))) { id = toPokemonId(Dex.data.Pokedex[i].species); } - let regexp = new RegExp(`\\b${id}\\b`, 'ig'); - let res = regexp.exec(parsed); + const regexp = new RegExp(`\\b${id}\\b`, 'ig'); + const res = regexp.exec(parsed); if (res) { - let num = Dex.data.Pokedex[i].num < 100 ? (Dex.data.Pokedex[i].num < 10 ? `00${Dex.data.Pokedex[i].num}` : `0${Dex.data.Pokedex[i].num}`) : Dex.data.Pokedex[i].num; + const num = Dex.data.Pokedex[i].num < 100 ? (Dex.data.Pokedex[i].num < 10 ? `00${Dex.data.Pokedex[i].num}` : `0${Dex.data.Pokedex[i].num}`) : Dex.data.Pokedex[i].num; return `${text.slice(0, res.index)}${text.slice(res.index, res.index + res[0].length)}${text.slice(res.index + res[0].length)}`; } } @@ -648,29 +569,30 @@ class GtsGiveaway { } } -/** @type {ChatCommands} */ -let commands = { +const cmds: ChatCommands = { // question giveaway. quiz: 'question', qg: 'question', question(target, room, user) { if (room.roomid !== 'wifi' || !target) return false; - // @ts-ignore if (room.giveaway) return this.errorReply("There is already a giveaway going on!"); - let [giver, ot, tid, fc, prize, question, ...answers] = target.split(target.includes('|') ? '|' : ',').map(param => param.trim()); + let [giver, ot, tid, fc, prize, question, ...answers] = target.split(target.includes('|') ? '|' : ',').map( + param => param.trim() + ); if (!(giver && ot && tid && fc && prize && question && answers.length)) return this.errorReply("Invalid arguments specified - /question giver | ot | tid | fc | prize | question | answer(s)"); tid = toID(tid); if (isNaN(parseInt(tid)) || tid.length < 5 || tid.length > 6) return this.errorReply("Invalid TID"); fc = toID(fc); if (!parseInt(fc) || fc.length !== 12) return this.errorReply("Invalid FC"); - let targetUser = Users.get(giver); + const targetUser = Users.get(giver); if (!targetUser || !targetUser.connected) return this.errorReply(`User '${giver}' is not online.`); - if (!user.can('warn', null, room) && !(user.can('broadcast', null, room) && user === targetUser)) return this.errorReply("/qg - Access denied."); + if (!user.can('warn', null, room) && !(user.can('broadcast', null, room) && user === targetUser)) { + return this.errorReply("/qg - Access denied."); + } if (!targetUser.autoconfirmed) return this.errorReply(`User '${targetUser.name}' needs to be autoconfirmed to give something away.`); if (Giveaway.checkBanned(room, targetUser)) return this.errorReply(`User '${targetUser.name}' is giveaway banned.`); - // @ts-ignore room.giveaway = new QuestionGiveaway(user, targetUser, room, ot, tid, fc, prize, question, answers); this.privateModAction(`(${user.name} started a question giveaway for ${targetUser.name})`); @@ -679,21 +601,17 @@ let commands = { changeanswer: 'changequestion', changequestion(target, room, user, conn, cmd) { if (room.roomid !== 'wifi') return false; - // @ts-ignore if (!room.giveaway) return this.errorReply("There is no giveaway going on at the moment."); - // @ts-ignore if (room.giveaway.type !== 'question') return this.errorReply("This is not a question giveaway."); target = target.trim(); if (!target) return this.errorReply("You must include a question or an answer."); - // @ts-ignore - room.giveaway.change(cmd.substr(6), target, user); + (room.giveaway as QuestionGiveaway).change(cmd.substr(6), target, user); }, showanswer: 'viewanswer', viewanswer(target, room, user) { if (room.roomid !== 'wifi') return false; - // @ts-ignore - let giveaway = room.giveaway; + const giveaway = room.giveaway as QuestionGiveaway; if (!giveaway) return this.errorReply("There is no giveaway going on at the moment."); if (giveaway.type !== 'question') return this.errorReply("This is not a question giveaway."); if (user.id !== giveaway.host.id && user.id !== giveaway.giver.id) return; @@ -705,12 +623,9 @@ let commands = { guess(target, room, user) { if (room.roomid !== 'wifi') return this.errorReply("This command can only be used in the Wi-Fi room."); if (!this.canTalk()) return; - // @ts-ignore if (!room.giveaway) return this.errorReply("There is no giveaway going on at the moment."); - // @ts-ignore if (room.giveaway.type !== 'question') return this.errorReply("This is not a question giveaway."); - // @ts-ignore - room.giveaway.guessAnswer(user, target); + (room.giveaway as QuestionGiveaway).guessAnswer(user, target); }, // lottery giveaway. @@ -718,7 +633,6 @@ let commands = { lotto: 'lottery', lottery(target, room, user) { if (room.roomid !== 'wifi' || !target) return false; - // @ts-ignore if (room.giveaway) return this.errorReply("There is already a giveaway going on!"); let [giver, ot, tid, fc, prize, winners] = target.split(target.includes('|') ? '|' : ',').map(param => param.trim()); @@ -727,9 +641,11 @@ let commands = { if (isNaN(parseInt(tid)) || tid.length < 5 || tid.length > 6) return this.errorReply("Invalid TID"); fc = toID(fc); if (!parseInt(fc) || fc.length !== 12) return this.errorReply("Invalid FC"); - let targetUser = Users.get(giver); + const targetUser = Users.get(giver); if (!targetUser || !targetUser.connected) return this.errorReply(`User '${giver}' is not online.`); - if (!user.can('warn', null, room) && !(user.can('broadcast', null, room) && user === targetUser)) return this.errorReply("/lg - Access denied."); + if (!user.can('warn', null, room) && !(user.can('broadcast', null, room) && user === targetUser)) { + return this.errorReply("/lg - Access denied."); + } if (!targetUser.autoconfirmed) return this.errorReply(`User '${targetUser.name}' needs to be autoconfirmed to give something away.`); if (Giveaway.checkBanned(room, targetUser)) return this.errorReply(`User '${targetUser.name}' is giveaway banned.`); @@ -739,7 +655,6 @@ let commands = { if (isNaN(numWinners) || numWinners < 1 || numWinners > 5) return this.errorReply("The lottery giveaway can have a minimum of 1 and a maximum of 5 winners."); } - // @ts-ignore room.giveaway = new LotteryGiveaway(user, targetUser, room, ot, tid, fc, prize, numWinners); this.privateModAction(`(${user.name} started a lottery giveaway for ${targetUser.name})`); @@ -753,8 +668,7 @@ let commands = { join(target, room, user, conn, cmd) { if (room.roomid !== 'wifi') return this.errorReply("This command can only be used in the Wi-Fi room."); if (!this.canTalk() || user.semilocked) return; - // @ts-ignore - let giveaway = room.giveaway; + const giveaway = room.giveaway as LotteryGiveaway; if (!giveaway) return this.errorReply("There is no giveaway going on at the moment."); if (giveaway.type !== 'lottery') return this.errorReply("This is not a lottery giveaway."); @@ -776,83 +690,67 @@ let commands = { new: 'start', start(target, room, user) { if (room.roomid !== 'wifi' || !target) return false; - // @ts-ignore if (room.gtsga) return this.errorReply("There is already a GTS giveaway going on!"); - let [giver, amountStr, summary, deposit, lookfor] = target.split(target.includes('|') ? '|' : ',').map(param => param.trim()); + const [giver, amountStr, summary, deposit, lookfor] = target.split(target.includes('|') ? '|' : ',').map( + param => param.trim() + ); if (!(giver && amountStr && summary && deposit && lookfor)) return this.errorReply("Invalid arguments specified - /gts start giver | amount | summary | deposit | lookfor"); - let amount = parseInt(amountStr); + const amount = parseInt(amountStr); if (!amount || amount < 20 || amount > 100) return this.errorReply("Please enter a valid amount. For a GTS giveaway, you need to give away at least 20 mons, and no more than 100."); - let targetUser = Users.get(giver); + const targetUser = Users.get(giver); if (!targetUser || !targetUser.connected) return this.errorReply(`User '${giver}' is not online.`); if (!this.can('warn', null, room)) return this.errorReply("Permission denied."); if (!targetUser.autoconfirmed) return this.errorReply(`User '${targetUser.name}' needs to be autoconfirmed to host a giveaway.`); if (Giveaway.checkBanned(room, targetUser)) return this.errorReply(`User '${targetUser.name}' is giveaway banned.`); - // @ts-ignore - room.gtsga = new GtsGiveaway(room, targetUser, amount, summary, deposit, lookfor); + room.gtsga = new GTSGiveaway(room, targetUser, amount, summary, deposit, lookfor); this.privateModAction(`(${user.name} started a GTS giveaway for ${targetUser.name} with ${amount} Pokémon)`); this.modlog('GTS GIVEAWAY', null, `for ${targetUser.getLastId()} with ${amount} Pokémon`); }, left(target, room, user) { if (room.roomid !== 'wifi') return false; - // @ts-ignore if (!room.gtsga) return this.errorReply("There is no GTS giveaway going on!"); - // @ts-ignore if (!user.can('warn', null, room) && user !== room.gtsga.giver) return this.errorReply("Only the host or a staff member can update GTS giveaways."); if (!target) { if (!this.runBroadcast()) return; - // @ts-ignore let output = `The GTS giveaway from ${room.gtsga.giver} has ${room.gtsga.left} Pokémon remaining!`; - // @ts-ignore if (room.gtsga.sent.length) output += `Last winners: ${room.gtsga.sent.join(', ')}`; return this.sendReply(output); } - let newamount = parseInt(target); + const newamount = parseInt(target); if (isNaN(newamount)) return this.errorReply("Please enter a valid amount."); - // @ts-ignore if (newamount > room.gtsga.left) return this.errorReply("The new amount must be lower than the old amount."); - // @ts-ignore if (newamount < room.gtsga.left - 1) this.modlog(`GTS GIVEAWAY`, null, `set from ${room.gtsga.left} to ${newamount} left`); - // @ts-ignore room.gtsga.updateLeft(newamount); }, sent(target, room, user) { if (room.roomid !== 'wifi') return false; - // @ts-ignore if (!room.gtsga) return this.errorReply("There is no GTS giveaway going on!"); - // @ts-ignore if (!user.can('warn', null, room) && user !== room.gtsga.giver) return this.errorReply("Only the host or a staff member can update GTS giveaways."); if (!target || target.length > 12) return this.errorReply("Please enter a valid IGN."); - // @ts-ignore room.gtsga.updateSent(target); }, full(target, room, user) { if (room.roomid !== 'wifi') return false; - // @ts-ignore if (!room.gtsga) return this.errorReply("There is no GTS giveaway going on!"); - // @ts-ignore if (!user.can('warn', null, room) && user !== room.gtsga.giver) return this.errorReply("Only the host or a staff member can update GTS giveaways."); - // @ts-ignore if (room.gtsga.noDeposits) return this.errorReply("The GTS giveaway was already set to not accept deposits."); - // @ts-ignore room.gtsga.stopDeposits(); }, end(target, room, user) { if (room.roomid !== 'wifi') return this.errorReply("This command can only be used in the Wi-Fi room."); - // @ts-ignore if (!room.gtsga) return this.errorReply("There is no GTS giveaway going on at the moment."); if (!this.can('warn', null, room)) return false; if (target && target.length > 300) { return this.errorReply("The reason is too long. It cannot exceed 300 characters."); } - // @ts-ignore const amount = room.gtsga.end(true); if (target) target = `: ${target}`; this.modlog('GTS END', null, `with ${amount} left${target}`); @@ -866,7 +764,7 @@ let commands = { if (!this.can('warn', null, room)) return false; target = this.splitTarget(target, false); - let targetUser = this.targetUser; + const targetUser = this.targetUser; if (!targetUser) return this.errorReply(`User '${this.targetUsername}' not found.`); if (target.length > 300) { return this.errorReply("The reason is too long. It cannot exceed 300 characters."); @@ -874,7 +772,6 @@ let commands = { if (Punishments.getRoomPunishType(room, this.targetUsername)) return this.errorReply(`User '${this.targetUsername}' is already punished in this room.`); Giveaway.ban(room, targetUser, target); - // @ts-ignore if (room.giveaway) room.giveaway.kickUser(targetUser); this.modlog('GIVEAWAYBAN', targetUser, target); if (target) target = ` (${target})`; @@ -886,7 +783,7 @@ let commands = { if (!this.can('warn', null, room)) return false; this.splitTarget(target, false); - let targetUser = this.targetUser; + const targetUser = this.targetUser; if (!targetUser) return this.errorReply(`User '${this.targetUsername}' not found.`); if (!Giveaway.checkBanned(room, targetUser)) return this.errorReply(`User '${this.targetUsername}' isn't banned from entering giveaways.`); @@ -897,15 +794,12 @@ let commands = { stop: 'end', end(target, room, user) { if (room.roomid !== 'wifi') return this.errorReply("This command can only be used in the Wi-Fi room."); - // @ts-ignore if (!room.giveaway) return this.errorReply("There is no giveaway going on at the moment."); - // @ts-ignore if (!this.can('warn', null, room) && user.id !== room.giveaway.host.id) return false; if (target && target.length > 300) { return this.errorReply("The reason is too long. It cannot exceed 300 characters."); } - // @ts-ignore room.giveaway.end(true); this.modlog('GIVEAWAY END', null, target); if (target) target = `: ${target}`; @@ -914,17 +808,14 @@ let commands = { rm: 'remind', remind(target, room, user) { if (room.roomid !== 'wifi') return this.errorReply("This command can only be used in the Wi-Fi room."); - // @ts-ignore - let giveaway = room.giveaway; + const giveaway = room.giveaway; if (!giveaway) return this.errorReply("There is no giveaway going on at the moment."); if (!this.runBroadcast()) return; if (giveaway.type === 'question') { if (giveaway.phase !== 'started') return this.errorReply("The giveaway has not started yet."); - // @ts-ignore - room.giveaway.send(room.giveaway.generateQuestion()); + giveaway.send((giveaway as QuestionGiveaway).generateQuestion()); } else { - // @ts-ignore - room.giveaway.display(); + (giveaway as LotteryGiveaway).display(); } }, count(target, room, user) { @@ -933,10 +824,10 @@ let commands = { if (!target) return this.errorReply("No mon entered - /giveaway count pokemon."); if (!this.runBroadcast()) return; - let count = stats[target]; + const count = stats[target]; if (!count) return this.sendReplyBox("This Pokémon has never been given away."); - let recent = count.filter(val => val + RECENT_THRESHOLD > Date.now()).length; + const recent = count.filter(val => val + RECENT_THRESHOLD > Date.now()).length; this.sendReplyBox(`This Pokémon has been given away ${Chat.count(count, "times")}, a total of ${Chat.count(recent, "times")} in the past month.`); }, @@ -949,34 +840,34 @@ let commands = { case 'staff': if (!this.can('broadcast', null, room)) return; reply = 'Staff commands:!giveaway count to broadcast this to the entire room