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 `${imgHTML}
${Chat.formatText(description, true).replace(/\n/g, `
`)}
`; } -exports.destroy = function () { +export const destroy = () => { clearTimeout(timeout); }; -/** @type {PageTable} */ -const pages = { +export const pages: PageTable = { async spotlights(query, user, connection) { this.title = 'Daily Spotlights'; this.extractRoom(); @@ -64,10 +58,10 @@ const pages = { if (!spotlights[this.room.roomid]) { buf += `

This room has no daily spotlights.

`; } else { - for (let key in spotlights[this.room.roomid]) { + for (const key in spotlights[this.room.roomid]) { buf += ``; 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 += ``; // @ts-ignore room is definitely a proper room here. if (!user.can('announce', null, this.room)) break; @@ -78,10 +72,8 @@ const pages = { return buf; }, }; -exports.pages = pages; -/** @type {ChatCommands} */ -const commands = { +export const commands: ChatCommands = { async removedaily(target, room, user) { if (!room.chatRoomData) return this.errorReply("This command is unavailable in temporary rooms."); let [key, rest] = target.split(','); @@ -122,27 +114,30 @@ const commands = { if (!this.can('announce', null, room)) return false; if (!rest.length) return this.parse('/help daily'); - let image, description; + let img; + let desc; if (rest[0].trim().startsWith('http://') || rest[0].trim().startsWith('https://')) { - [image, ...rest] = rest; - image = image.trim(); - const ret = await Chat.getImageDimensions(image); - if (ret.err) return this.errorReply(`Invalid image url: ${image}`); + [img, ...rest] = rest; + img = img.trim(); + const ret = await Chat.getImageDimensions(img); + if (ret.err) return this.errorReply(`Invalid image url: ${img}`); } - description = rest.join(','); - if (Chat.stripFormatting(description).length > 500) return this.errorReply("Descriptions can be at most 500 characters long."); - const obj = {image: image || null, description: description}; + desc = rest.join(','); + if (Chat.stripFormatting(desc).length > 500) { + return this.errorReply("Descriptions can be at most 500 characters long."); + } + const obj = {image: img, description: desc}; if (!spotlights[room.roomid]) spotlights[room.roomid] = {}; if (!spotlights[room.roomid][key]) spotlights[room.roomid][key] = []; if (cmd === 'setdaily') { spotlights[room.roomid][key].shift(); spotlights[room.roomid][key].unshift(obj); - this.modlog('SETDAILY', key, `${image ? `${image}, ` : ''}${description}`); + this.modlog('SETDAILY', key, `${img ? `${img}, ` : ''}${desc}`); this.privateModAction(`${user.name} set the daily ${key}.`); } else { spotlights[room.roomid][key].push(obj); - this.modlog('QUEUEDAILY', key, `${image ? `${image}, ` : ''}${description}`); + this.modlog('QUEUEDAILY', key, `${img ? `${img}, ` : ''}${desc}`); this.privateModAction(`${user.name} queued a daily ${key}.`); } @@ -150,7 +145,7 @@ const commands = { }, async daily(target, room, user) { if (!room.chatRoomData) return this.errorReply("This command is unavailable in temporary rooms."); - let key = toID(target); + const key = toID(target); if (!key) return this.parse('/help daily'); if (!spotlights[room.roomid] || !spotlights[room.roomid][key]) { @@ -160,7 +155,7 @@ const commands = { if (!this.runBroadcast()) return; const {image, description} = spotlights[room.roomid][key][0]; - const html = await renderSpotlight(image, description); + const html = await renderSpotlight(description, image); this.sendReplyBox(html); room.update(); @@ -181,8 +176,6 @@ const commands = { ], }; -exports.commands = commands; - process.nextTick(() => { Chat.multiLinePattern.register('/(queue|set)daily '); }); diff --git a/server/chat-plugins/room-faqs.js b/server/chat-plugins/room-faqs.ts similarity index 86% rename from server/chat-plugins/room-faqs.js rename to server/chat-plugins/room-faqs.ts index d4c6279c7e..1e6d833b86 100644 --- a/server/chat-plugins/room-faqs.js +++ b/server/chat-plugins/room-faqs.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 ROOMFAQ_FILE = 'config/chat-plugins/faqs.json'; const MAX_ROOMFAQ_LENGTH = 8192; -/** @type {{[k: string]: {[k: string]: string}}} */ -let roomFaqs = {}; +let roomFaqs: {[k: string]: {[k: string]: string}} = {}; try { - roomFaqs = require(`../../${ROOMFAQ_FILE}`); + roomFaqs = JSON.parse(fs.readFileSync(`../../${ROOMFAQ_FILE}`).toString()); } catch (e) { if (e.code !== 'MODULE_NOT_FOUND' && e.code !== 'ENOENT') throw e; } @@ -20,28 +19,25 @@ function saveRoomFaqs() { } /** - * @param {RoomID} roomid - * @param {string} key - * * Aliases are implemented as a "regular" FAQ entry starting with a >. EX: {a: "text", b: ">a"} - * This is done to allow easy checking whether a key is associated with a value or alias as well as preserve backwards compatibility. + * This is done to allow easy checking whether a key is associated with + * a value or alias as well as preserve backwards compatibility. */ -function getAlias(roomid, key) { +function getAlias(roomid: RoomID, key: string) { if (!roomFaqs[roomid]) return false; - let value = roomFaqs[roomid][key]; + const value = roomFaqs[roomid][key]; if (value && value[0] === '>') return value.substr(1); return false; } -/** @type {ChatCommands} */ -const commands = { +export const commands: ChatCommands = { addfaq(target, room, user, connection) { if (!this.can('ban', null, room)) return false; if (!room.chatRoomData) return this.errorReply("This command is unavailable in temporary rooms."); if (!target) return this.parse('/help roomfaq'); target = target.trim(); - let input = this.filter(target); + const input = this.filter(target); if (target !== input) return this.errorReply("You are not allowed to use fitered words in roomfaq entries."); let [topic, ...rest] = input.split(','); @@ -64,12 +60,16 @@ const commands = { if (!this.canTalk()) return this.errorReply("You cannot do this while unable to talk."); if (!this.can('ban', null, room)) return false; if (!room.chatRoomData) return this.errorReply("This command is unavailable in temporary rooms."); - let topic = toID(target); + const topic = toID(target); if (!topic) return this.parse('/help roomfaq'); if (!(roomFaqs[room.roomid] && roomFaqs[room.roomid][topic])) return this.errorReply("Invalid topic."); delete roomFaqs[room.roomid][topic]; - Object.keys(roomFaqs[room.roomid]).filter(val => getAlias(room.roomid, val) === topic).map(val => delete roomFaqs[room.roomid][val]); + Object.keys(roomFaqs[room.roomid]).filter( + val => getAlias(room.roomid, val) === topic + ).map( + val => delete roomFaqs[room.roomid][val] + ); if (!Object.keys(roomFaqs[room.roomid]).length) delete roomFaqs[room.roomid]; saveRoomFaqs(); this.privateModAction(`(${user.name} removed the FAQ for '${topic}')`); @@ -79,7 +79,7 @@ const commands = { if (!this.canTalk()) return this.errorReply("You cannot do this while unable to talk."); if (!this.can('ban', null, room)) return false; if (!room.chatRoomData) return this.errorReply("This command is unavailable in temporary rooms."); - let [alias, topic] = target.split(',').map(val => toID(val)); + const [alias, topic] = target.split(',').map(val => toID(val)); if (!(alias && topic)) return this.parse('/help roomfaq'); if (alias.length > 25) return this.errorReply("FAQ topics should not exceed 25 characters."); @@ -95,8 +95,7 @@ const commands = { rfaq: 'roomfaq', roomfaq(target, room, user, connection, cmd) { if (!roomFaqs[room.roomid]) return this.errorReply("This room has no FAQ topics."); - /** @type {string} */ - let topic = toID(target); + let topic: string = toID(target); if (topic === 'constructor') return false; if (!topic) return this.sendReplyBox(`List of topics in this room: ${Object.keys(roomFaqs[room.roomid]).filter(val => !getAlias(room.roomid, val)).sort((a, b) => a.localeCompare(b)).map(rfaq => ``).join(', ')}`); if (!roomFaqs[room.roomid][topic]) return this.errorReply("Invalid topic."); @@ -124,8 +123,6 @@ const commands = { ], }; -exports.commands = commands; - process.nextTick(() => { Chat.multiLinePattern.register('/addfaq '); }); diff --git a/server/chat-plugins/wifi.js b/server/chat-plugins/wifi.ts similarity index 72% rename from server/chat-plugins/wifi.js rename to server/chat-plugins/wifi.ts index 93c16ebed8..2d5dcfa3af 100644 --- a/server/chat-plugins/wifi.js +++ b/server/chat-plugins/wifi.ts @@ -1,13 +1,13 @@ /** -* Wi-Fi chat-plugin. Only works in a room with id 'wifi' -* Handles giveaways in the formats: question, lottery, gts -* Written by bumbadadabum, based on the original plugin as written by Codelegend, SilverTactic, DanielCranham -**/ + * Wi-Fi chat-plugin. Only works in a room with id 'wifi' + * Handles giveaways in the formats: question, lottery, gts + * Written by Asheviere, based on the original plugin as written by Codelegend, SilverTactic, DanielCranham + */ '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'; Punishments.roomPunishmentTypes.set('GIVEAWAYBAN', 'banned from giveaways'); @@ -16,10 +16,9 @@ const RECENT_THRESHOLD = 30 * 24 * 60 * 60 * 1000; const STATS_FILE = 'config/chat-plugins/wifi.json'; -/** @type {{[k: string]: number[]}} */ -let stats = {}; +let stats: {[k: string]: number[]} = {}; try { - stats = require(`../../${STATS_FILE}`); + stats = JSON.parse(fs.readFileSync(`../../${STATS_FILE}`).toString()); } catch (e) { if (e.code !== 'MODULE_NOT_FOUND' && e.code !== 'ENOENT') throw e; } @@ -29,27 +28,29 @@ function saveStats() { FS(STATS_FILE).writeUpdate(() => JSON.stringify(stats)); } -/** - * @param {string} str - */ -function toPokemonId(str) { +function toPokemonId(str: string) { return str.toLowerCase().replace(/é/g, 'e').replace(/[^a-z0-9 -/]/g, ''); } class Giveaway { - /** - * @param {User} host - * @param {User} giver - * @param {ChatRoom | GameRoom} room - * @param {string} ot - * @param {string} tid - * @param {string} fc - * @param {string} prize - */ - constructor(host, giver, room, ot, tid, fc, prize) { - // The massive amounts of typescipt ignores are to work around something that is impossible to implement in the current typescipt version, but should be available soon. - // I don't feel the need to fully overhaul the code structure to temporarily shut tsc up. I would rather use ignores for the time being to accomplish that. + gaNumber: number; + host: User; + giver: User; + room: ChatRoom | GameRoom; + ot: string; + tid: string; + fc: string; + prize: string; + phase: string; + joined: {[k: string]: string}; + timer: NodeJS.Timer | null; + monIDs: Set; + sprite: string; + constructor( + host: User, giver: User, room: ChatRoom | GameRoom, + ot: string, tid: string, fc: string, prize: string + ) { this.gaNumber = ++room.gameNumber; this.host = host; this.giver = giver; @@ -61,34 +62,23 @@ class Giveaway { this.prize = prize; this.phase = 'pending'; - /** @type {{[k: string]: string}} */ this.joined = {}; - /** @type {NodeJS.Timer?} */ this.timer = null; - // This seems wrong but I can't find a better way to do this. - /** @type {Set} */ - // @ts-ignore - this.monIds = null; + this.monIDs = new Set(); this.sprite = ''; - [this.monIds, this.sprite] = Giveaway.getSprite(prize); + [this.monIDs, this.sprite] = Giveaway.getSprite(prize); } generateReminder() {} - /** - * @param {string} content - */ - send(content) { + send(content: string) { this.room.add(`|uhtml|giveaway${this.gaNumber}${this.phase}|
${content}
`); this.room.update(); } - /** - * @param {string} content - */ - changeUhtml(content) { + changeUhtml(content: string) { this.room.add(`|uhtmlchange|giveaway${this.gaNumber}${this.phase}|
${content}
`); this.room.update(); } @@ -100,22 +90,16 @@ class Giveaway { } } - /** - * @param {User} user - */ - checkJoined(user) { - for (let ip in this.joined) { + checkJoined(user: User) { + for (const ip in this.joined) { if (user.latestIp === ip) return ip; if (this.joined[ip] in user.prevNames) return this.joined[ip]; } return false; } - /** - * @param {User} user - */ - kickUser(user) { - for (let ip in this.joined) { + kickUser(user: User) { + for (const ip in this.joined) { if (user.latestIp === ip || this.joined[ip] in user.prevNames) { user.sendTo(this.room, `|uhtmlchange|giveaway${this.gaNumber}${this.phase}|
${this.generateReminder()}
`); delete this.joined[ip]; @@ -123,57 +107,36 @@ class Giveaway { } } - /** - * @param {User} user - */ - checkExcluded(user) { + checkExcluded(user: User) { if (user === this.giver || user.latestIp in this.giver.ips || toID(user) in this.giver.prevNames) return true; return false; } - /** - * @param {ChatRoom | GameRoom} room - * @param {User} user - */ - static checkBanned(room, user) { + static checkBanned(room: ChatRoom | GameRoom, user: User) { return Punishments.getRoomPunishType(room, toID(user)) === 'GIVEAWAYBAN'; } - /** - * @param {ChatRoom | GameRoom} room - * @param {User} user - * @param {string} reason - */ - static ban(room, user, reason) { + static ban(room: ChatRoom | GameRoom, user: User, reason: string) { Punishments.roomPunish(room, user, ['GIVEAWAYBAN', toID(user), Date.now() + BAN_DURATION, reason]); } - /** - * @param {ChatRoom | GameRoom} room - * @param {User} user - */ - static unban(room, user) { + static unban(room: ChatRoom | GameRoom, user: User) { Punishments.roomUnpunish(room, toID(user), 'GIVEAWAYBAN', false); } - /** - * @param {string} text - * - * @return {[Set, string]} - */ - static getSprite(text) { + static getSprite(text: string): [Set, string] { text = toPokemonId(text); - let mons = new Map(); + const mons = new Map(); let output = ''; - let monIds = new Set(); - for (let i in Dex.data.Pokedex) { + const monIDs: Set = new Set(); + 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`); + const regexp = new RegExp(`\\b${id}\\b`); if (regexp.test(text)) { - let mon = Dex.getTemplate(i); + const mon = Dex.getTemplate(i); mons.set(mon.baseSpecies, mon); } } @@ -189,45 +152,49 @@ class Giveaway { for (const [key, value] of mons) { let spriteid = value.spriteid; if (value.otherForms) { - for (let i = 0; i < value.otherForms.length; i++) { - if (text.includes(value.otherForms[i])) { - spriteid += '-' + value.otherForms[i].substr(key.length); + for (const form of value.otherForms) { + if (text.includes(form)) { + spriteid += '-' + form.substr(key.length); break; // We don't want to end up with deerling-summer-spring } } } if (value.otherFormes) { - for (let i = 0; i < value.otherFormes.length; i++) { + for (const forme of value.otherFormes) { // Allow "alolan " to match as well. - if (value.otherFormes[i].endsWith('alola')) { + if (forme.endsWith('alola')) { if (/alolan?/.test(text)) { spriteid += '-alola'; break; } } - if (text.includes(value.otherFormes[i])) { - spriteid += '-' + value.otherFormes[i].substr(key.length); + // Allow "galarian " to match as well. + if (forme.endsWith('galar')) { + if (/galar(ian)?/.test(text)) { + spriteid += '-galar'; + break; + } + } + if (text.includes(forme)) { + spriteid += '-' + forme.substr(key.length); break; // We don't want to end up with landorus-therian-therian } } } - monIds.add(spriteid); + monIDs.add(spriteid); if (mons.size > 1) { output += ``; } else { - let shiny = (text.includes("shiny") && !text.includes("shinystone") ? '-shiny' : ''); - output += ``; + const shiny = (text.includes("shiny") && !text.includes("shinystone") ? '-shiny' : ''); + output += ``; } } } - return [monIds, output]; + return [monIDs, output]; } - /** - * @param {Set} monIds - */ - static updateStats(monIds) { - for (let mon of monIds) { + static updateStats(monIDs: Set) { + for (const mon of monIDs) { if (!stats[mon]) stats[mon] = []; stats[mon].push(Date.now()); @@ -236,10 +203,7 @@ class Giveaway { saveStats(); } - /** - * @param {string} rightSide - */ - generateWindow(rightSide) { + generateWindow(rightSide: string) { return `

It's giveaway time!

` + `

Giveaway started by ${Chat.escapeHTML(this.host.name)}

` + `

${key}:

${i ? i : 'Current'}${html}
` + @@ -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('

The giveaway was forcibly ended.

'); @@ -358,60 +311,48 @@ class QuestionGiveaway extends Giveaway { this.winner.sendTo(this.room, `|raw|You have won the giveaway. PM ${Chat.escapeHTML(this.giver.name)} (FC: ${this.fc}) to claim your prize!`); if (this.winner.connected) this.winner.popup(`You have won the giveaway. PM **${this.giver.name}** (FC: ${this.fc}) to claim your prize!`); if (this.giver.connected) this.giver.popup(`${this.winner.name} has won your question giveaway!`); - Giveaway.updateStats(this.monIds); + Giveaway.updateStats(this.monIDs); } } - // @ts-ignore delete this.room.giveaway; } - /** - * @param {string} str - * - * @return {string} - */ - static sanitize(str) { + static sanitize(str: string) { return str.toLowerCase().replace(/[^a-z0-9 .-]+/ig, "").trim(); } - /** - * @param {string[]} answers - */ - static sanitizeAnswers(answers) { - return answers.map(val => QuestionGiveaway.sanitize(val)).filter((val, index, array) => toID(val).length && array.indexOf(val) === index); + static sanitizeAnswers(answers: string[]) { + return answers.map( + val => QuestionGiveaway.sanitize(val) + ).filter( + (val, index, array) => toID(val).length && array.indexOf(val) === index + ); } - /** - * @param {User} user - */ - checkExcluded(user) { + checkExcluded(user: User) { if (user === this.host || user.latestIp in this.host.ips || toID(user) in this.host.prevNames) return true; return super.checkExcluded(user); } } -class LotteryGiveaway extends Giveaway { - /** - * @param {User} host - * @param {User} giver - * @param {ChatRoom} room - * @param {string} ot - * @param {string} tid - * @param {string} fc - * @param {string} prize - * @param {number} winners - */ - constructor(host, giver, room, ot, tid, fc, prize, winners) { +export class LotteryGiveaway extends Giveaway { + type: string; + winners: User[]; + maxWinners: number; + + constructor( + host: User, giver: User, room: ChatRoom | GameRoom, ot: string, + tid: string, fc: string, prize: string, winners: number + ) { super(host, giver, room, ot, tid, fc, prize); this.type = 'lottery'; this.phase = 'pending'; - /** @type {User[]} */ this.winners = []; - this.maxwinners = winners || 1; + this.maxWinners = winners || 1; this.send(this.generateReminder(false)); @@ -419,17 +360,17 @@ class LotteryGiveaway extends Giveaway { } generateReminder(joined = false) { - let cmd = (joined ? 'Leave' : 'Join'); - let button = ``; - return this.generateWindow(`The lottery drawing will occur in 2 minutes, and with ${Chat.count(this.maxwinners, "winners")}!
${button}

`); + const cmd = (joined ? 'Leave' : 'Join'); + const button = ``; + return this.generateWindow(`The lottery drawing will occur in 2 minutes, and with ${Chat.count(this.maxWinners, "winners")}!
${button}

`); } display() { - let joined = this.generateReminder(true); - let notJoined = this.generateReminder(); + const joined = this.generateReminder(true); + const notJoined = this.generateReminder(); - for (let i in this.room.users) { - let thisUser = this.room.users[i]; + for (const i in this.room.users) { + const thisUser = this.room.users[i]; if (this.checkJoined(thisUser)) { thisUser.sendTo(this.room, `|uhtmlchange|giveaway${this.gaNumber}${this.phase}|
${joined}
`); } else { @@ -438,10 +379,7 @@ class LotteryGiveaway extends Giveaway { } } - /** - * @param {User} user - */ - addUser(user) { + addUser(user: User) { if (this.phase !== 'pending') return user.sendTo(this.room, "The join phase of the lottery giveaway has ended."); if (!user.named) return user.sendTo(this.room, "You need to choose a name before joining a lottery giveaway."); @@ -454,13 +392,10 @@ class LotteryGiveaway extends Giveaway { user.sendTo(this.room, "You have successfully joined the lottery giveaway."); } - /** - * @param {User} user - */ - removeUser(user) { + removeUser(user: User) { if (this.phase !== 'pending') return user.sendTo(this.room, "The join phase of the lottery giveaway has ended."); if (!this.checkJoined(user)) return user.sendTo(this.room, "You have not joined the lottery giveaway."); - for (let ip in this.joined) { + for (const ip in this.joined) { if (ip === user.latestIp || this.joined[ip] === user.id) { delete this.joined[ip]; } @@ -472,16 +407,15 @@ class LotteryGiveaway extends Giveaway { drawLottery() { this.clearTimer(); - let userlist = Object.values(this.joined); - if (userlist.length < this.maxwinners) { + const userlist = Object.values(this.joined); + if (userlist.length < this.maxWinners) { this.changeUhtml('

The giveaway was forcibly ended.

'); - // @ts-ignore delete this.room.giveaway; return this.room.send("The giveaway has been forcibly ended as there are not enough participants."); } - while (this.winners.length < this.maxwinners) { - let winner = Users.get(userlist.splice(Math.floor(Math.random() * userlist.length), 1)[0]); + while (this.winners.length < this.maxWinners) { + const winner = Users.get(userlist.splice(Math.floor(Math.random() * userlist.length), 1)[0]); if (!winner) continue; this.winners.push(winner); } @@ -496,47 +430,50 @@ class LotteryGiveaway extends Giveaway { } else { this.changeUhtml(`

The giveaway has ended! Scroll down to see the winner${Chat.plural(this.winners)}.

`); this.phase = 'ended'; - let winnerNames = this.winners.map(winner => winner.name).join(', '); + const winnerNames = this.winners.map(winner => winner.name).join(', '); this.room.modlog(`(wifi) GIVEAWAY WIN: ${winnerNames} won ${this.giver.name}'s giveaway for "${this.prize}" (OT: ${this.ot} TID: ${this.tid} FC: ${this.fc})`); this.send(this.generateWindow(`

Lottery Draw

${Object.keys(this.joined).length} users joined the giveaway.
Our lucky winner${Chat.plural(this.winners)}: ${Chat.escapeHTML(winnerNames)}! Congratulations!

`)); - for (let i = 0; i < this.winners.length; i++) { - this.winners[i].sendTo(this.room, `|raw|You have won the lottery giveaway! PM ${this.giver.name} (FC: ${this.fc}) to claim your prize!`); - if (this.winners[i].connected) this.winners[i].popup(`You have won the lottery giveaway! PM **${this.giver.name}** (FC: ${this.fc}) to claim your prize!`); + for (const winner of this.winners) { + winner.sendTo(this.room, `|raw|You have won the lottery giveaway! PM ${this.giver.name} (FC: ${this.fc}) to claim your prize!`); + if (winner.connected) winner.popup(`You have won the lottery giveaway! PM **${this.giver.name}** (FC: ${this.fc}) to claim your prize!`); } if (this.giver.connected) this.giver.popup(`The following users have won your lottery giveaway:\n${winnerNames}`); - Giveaway.updateStats(this.monIds); + Giveaway.updateStats(this.monIDs); } - // @ts-ignore delete this.room.giveaway; } } -class GtsGiveaway { - /** - * @param {ChatRoom} room - * @param {User} giver - * @param {number} amount - * @param {string} summary - * @param {string} deposit - * @param {string} lookfor - */ - constructor(room, giver, amount, summary, deposit, lookfor) { +export class GTSGiveaway { + gtsNumber: number; + room: ChatRoom | GameRoom; + giver: User; + left: number; + summary: string; + deposit: string; + lookfor: string; + monIDs: Set; + sprite: string; + sent: string[]; + noDeposits: boolean; + timer: NodeJS.Timer | null; + + constructor( + room: ChatRoom | GameRoom, giver: User, amount: number, + summary: string, deposit: string, lookfor: string + ) { this.gtsNumber = ++room.gameNumber; this.room = room; this.giver = giver; this.left = amount; this.summary = summary; - this.deposit = GtsGiveaway.linkify(Chat.escapeHTML(deposit)); + this.deposit = GTSGiveaway.linkify(Chat.escapeHTML(deposit)); this.lookfor = lookfor; - // This seems wrong but I can't find a better way to do this. - /** @type {Set} */ - // @ts-ignore - this.monIds = null; + this.monIDs = new Set(); this.sprite = ''; - [this.monIds, this.sprite] = Giveaway.getSprite(this.summary); + [this.monIDs, this.sprite] = Giveaway.getSprite(this.summary); - /** @type {string[]} */ this.sent = []; this.noDeposits = false; @@ -544,18 +481,12 @@ class GtsGiveaway { this.send(this.generateWindow()); } - /** - * @param {string} content - */ - send(content) { + send(content: string) { this.room.add(`|uhtml|gtsga${this.gtsNumber}|
${content}
`); this.room.update(); } - /** - * @param {string} content - */ - changeUhtml(content) { + changeUhtml(content: string) { this.room.add(`|uhtmlchange|gtsga${this.gtsNumber}|
${content}
`); this.room.update(); } @@ -568,8 +499,8 @@ class GtsGiveaway { } generateWindow() { - let sentModifier = this.sent.length ? 5 : 0; - let rightSide = this.noDeposits ? `More Pokémon have been deposited than there are prizes in this giveaway and new deposits will not be accepted. If you have already deposited a Pokémon, please be patient, and do not withdraw your Pokémon.` : `To participate, deposit ${this.deposit} into the GTS and look for ${Chat.escapeHTML(this.lookfor)}`; + const sentModifier = this.sent.length ? 5 : 0; + const rightSide = this.noDeposits ? `More Pokémon have been deposited than there are prizes in this giveaway and new deposits will not be accepted. If you have already deposited a Pokémon, please be patient, and do not withdraw your Pokémon.` : `To participate, deposit ${this.deposit} into the GTS and look for ${Chat.escapeHTML(this.lookfor)}`; return `

There is a GTS giveaway going on!

` + `

Hosted by: ${Chat.escapeHTML(this.giver.name)} | Left: ${this.left}

` + `
${this.sprite}

Giver: ${this.giver}

${Chat.formatText(this.prize, true)}
OT: ${Chat.escapeHTML(this.ot)}, TID: ${this.tid}
` + @@ -578,20 +509,14 @@ class GtsGiveaway { `
${rightSide}
`; } - /** - * @param {number} number - */ - updateLeft(number) { - this.left = number; + updateLeft(num: number) { + this.left = num; if (this.left < 1) return this.end(); this.changeUhtml(this.generateWindow()); } - /** - * @param {string} ign - */ - updateSent(ign) { + updateSent(ign: string) { this.left--; if (this.left < 1) return this.end(); @@ -618,29 +543,25 @@ class GtsGiveaway { this.changeUhtml(`

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:
' + - '- question or qg User | OT | TID | Friend Code | Prize | Question | Answer[ | Answer2 | Answer3] - Start a new question giveaway (voices can only host for themselves, staff can for all users) (Requires: + % @ # & ~)
' + - '- lottery or lg User | OT | TID | Friend Code | Prize[| Number of Winners] - Starts a lottery giveaway (voices can only host for themselves, staff can for all users) (Requires: + % @ # & ~)
' + - '- changequestion - Changes the question of a question giveaway (Requires: giveaway host)
' + - '- changeanswer - Changes the answer of a question giveaway (Requires: giveaway host)
' + + '- question or qg User | OT | TID | Friend Code | Prize | Question | Answer[ | Answer2 | Answer3] - Start a new question giveaway (voices can only host for themselves, staff can for all users) (Requires: + % @ # & ~)
' + + '- lottery or lg User | OT | TID | Friend Code | Prize[| Number of Winners] - Starts a lottery giveaway (voices can only host for themselves, staff can for all users) (Requires: + % @ # & ~)
' + + '- changequestion - Changes the question of a question giveaway (Requires: giveaway host)
' + + '- changeanswer - Changes the answer of a question giveaway (Requires: giveaway host)
' + '- viewanswer - Shows the answer in a question giveaway (only to giveaway host/giver)
' + '- ban - Temporarily bans a user from entering giveaways (Requires: % @ # & ~)
' + - '- end - Forcibly ends the current giveaway (Requires: % @ # & ~)
' + + '- end - Forcibly ends the current giveaway (Requires: % @ # & ~)
' + '- count Mon - Displays how often a certain mon has been given away. Use !giveaway count to broadcast this to the entire room
'; break; case 'gts': if (!this.can('broadcast', null, room)) return; reply = 'GTS giveaway commands:
' + - '- gts start User | Amount | Summary of given mon | What to deposit | What to look for - Starts a gts giveaway (Requires: % @ # & ~)
' + + '- gts start User | Amount | Summary of given mon | What to deposit | What to look for - Starts a gts giveaway (Requires: % @ # & ~)
' + '- gts left Amount - Updates the amount left for the current GTS giveaway. Without an amount specified, shows how many Pokémon are left, and who the latest winners are.
' + '- gts sent IGN - Adds an ign to the list of latest winners, and updates left count accordingly.
' + '- gts full - Signifies enough mons have been received, and will update the GTS giveaway to reflect that.
' + - '- gts end - Forcibly ends the current gts giveaway (Requires: % @ # & ~)
'; + '- gts end - Forcibly ends the current gts giveaway (Requires: % @ # & ~)
'; break; case 'game': case 'giveaway': case 'user': if (!this.runBroadcast()) return; reply = 'Giveaway participation commands: (start with /giveaway, except for /ga)
' + - '- guess or /ga answer - Guesses the answer for a question giveaway
' + - '- viewanswer - Shows the answer in a question giveaway (only to host/giver)
' + - '- remind - Shows the details of the current giveaway (can be broadcast)
' + - '- join or joinlottery - Joins a lottery giveaway
' + - '- leave or leavelottery - Leaves a lottery giveaway
'; + '- guess or /ga answer - Guesses the answer for a question giveaway
' + + '- viewanswer - Shows the answer in a question giveaway (only to host/giver)
' + + '- remind - Shows the details of the current giveaway (can be broadcast)
' + + '- join or joinlottery - Joins a lottery giveaway
' + + '- leave or leavelottery - Leaves a lottery giveaway
'; break; default: if (!this.runBroadcast()) return; @@ -989,13 +880,13 @@ let commands = { }, }; -exports.commands = { - 'giveaway': commands, - 'ga': commands.guess, - 'gh': commands.help, - 'qg': commands.question, - 'lg': commands.lottery, - 'gts': commands.gts, - 'left': commands.left, - 'sent': commands.sent, +export const commands = { + giveaway: cmds, + ga: cmds.guess, + gh: cmds.help, + qg: cmds.question, + lg: cmds.lottery, + gts: cmds.gts, + left: cmds.left, + sent: cmds.sent, }; diff --git a/server/rooms.ts b/server/rooms.ts index 6a7e9d1df2..6c5e12bdd5 100644 --- a/server/rooms.ts +++ b/server/rooms.ts @@ -23,6 +23,7 @@ const RETRY_AFTER_LOGIN = null; import {FS} from '../lib/fs'; import {WriteStream} from '../lib/streams'; +import {GTSGiveaway, LotteryGiveaway, QuestionGiveaway} from './chat-plugins/wifi'; import {PM as RoomBattlePM, RoomBattle, RoomBattlePlayer, RoomBattleTimer} from "./room-battle"; import {RoomGame, RoomGamePlayer} from './room-game'; import {Roomlogs} from './roomlogs'; @@ -103,6 +104,8 @@ export abstract class BasicRoom { unoDisabled: boolean; blackjackDisabled: boolean; hangmanDisabled: boolean; + giveaway: QuestionGiveaway | LotteryGiveaway | null; + gtsga: GTSGiveaway | null; toursEnabled: '%' | boolean; tourAnnouncements: boolean; privacySetter: Set | null; @@ -161,6 +164,8 @@ export abstract class BasicRoom { this.unoDisabled = false; this.blackjackDisabled = false; this.hangmanDisabled = false; + this.giveaway = null; + this.gtsga = null; this.toursEnabled = false; this.tourAnnouncements = false; this.privacySetter = null; diff --git a/tsconfig.json b/tsconfig.json index d8a835c886..2e9604db7f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,12 +25,8 @@ "./server/tournaments/*", "./server/chat-commands/*.ts", "./server/chat-plugins/*.ts", - "./server/chat-plugins/daily-spotlight.js", - "./server/chat-plugins/mafia-data.js", - "./server/chat-plugins/room-faqs.js", "./server/chat-plugins/thecafe.js", "./server/chat-plugins/thing-of-the-day.js", - "./server/chat-plugins/wifi.js", "./sim/**/*", "./tools/set-import/*.ts" ]