mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-03-21 17:25:10 -05:00
Typescript Daily Spotlight, RoomFAQs, and Wi-Fi (#6373)
This commit is contained in:
parent
d6fffde560
commit
2ea84725e2
|
|
@ -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}`);
|
||||
|
|
|
|||
|
|
@ -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 `<table style="text-align:center;margin:auto"><tr><td style="padding-right:10px;">${Chat.formatText(description, true).replace(/\n/g, `<br />`)}</td>${imgHTML}</tr></table>`;
|
||||
}
|
||||
|
||||
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 += `<p>This room has no daily spotlights.</p></div>`;
|
||||
} else {
|
||||
for (let key in spotlights[this.room.roomid]) {
|
||||
for (const key in spotlights[this.room.roomid]) {
|
||||
buf += `<table style="margin-bottom:30px;"><th colspan="2"><h3>${key}:</h3></th>`;
|
||||
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 += `<tr><td>${i ? i : 'Current'}</td><td>${html}</td></tr>`;
|
||||
// @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 ');
|
||||
});
|
||||
|
|
@ -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 => `<button class="button" name="send" value="/viewfaq ${rfaq}">${rfaq}</button>`).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 ');
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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<ID> | 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;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
]
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user