From b3b1efdab7ff2677cebefca32c6613cff5783d7d Mon Sep 17 00:00:00 2001 From: "Meijer,L. (Lucas)" Date: Sat, 9 Nov 2024 20:10:19 +0100 Subject: [PATCH 1/2] Fixing some Mafia Type errors --- server/chat-plugins/mafia.ts | 245 +++++++++++++++++++++++++++++++++-- 1 file changed, 234 insertions(+), 11 deletions(-) diff --git a/server/chat-plugins/mafia.ts b/server/chat-plugins/mafia.ts index 090bf2e55c9c..6eb3b5af2a27 100644 --- a/server/chat-plugins/mafia.ts +++ b/server/chat-plugins/mafia.ts @@ -141,6 +141,63 @@ function writeFile(path: string, data: AnyObject) { )); } +function mafiaSearch(entries: [string, MafiaDataAlignment | MafiaDataRole | MafiaDataTheme | MafiaDataIDEA | MafiaDataTerm][], + searchTarget: string, searchType: keyof MafiaData) { + if (typeof (entries) === 'undefined') return entries; + if (searchType === `aliases`) return entries; + + if (searchTarget.length === 0) return entries; + const negation = searchTarget.startsWith('!'); + + if (negation) searchTarget = searchTarget.substring(1).trim(); + const entriesCopy = entries.slice(); + + const alias = toID((toID(searchTarget) in MafiaData[`aliases`]) ? + MafiaData[`aliases`][toID(searchTarget)] : searchTarget); + + if (searchType === `themes` && searchTarget.includes(`players`) && searchTarget.includes(`pl`)) { + const inequalities = ['<=', '>=', '=', '<', '>']; + const inequality = inequalities.find(x => searchTarget.includes(x)); + if (!inequality) return entries; // this.errorReply(`Please provide a valid inequality for the players.`); + + const players = searchTarget.split(inequality)[1].trim(); + if (((players !== null) && + (players !== '') && + !isNaN(Number(players)))) { + if (inequality === '=') { + entries = entries.filter(([key]) => players in MafiaData[`themes`][key]); + } else if (inequality === '<' || inequality === '<=') { + entries = entries.filter(([key]) => ([...Array(+players + (inequality === '<=' ? +1 : +0)).keys()]) + .some(playerCount => playerCount in (MafiaData[`themes`][key]))); + } else if (inequality === '>' || inequality === '>=') { + entries = entries.filter(([key]) => ([...Array(50 - Number(players)).keys()] + .map(num => +num + +players + (inequality === '>=' ? +0 : +1))) + .some(playerCount => playerCount in (MafiaData[`themes`][key]))); + } + } else { + return entries; + } + } else if (searchType === `themes` && alias in MafiaData[`roles`]) { + entries = entries.filter(([key, data]) => ([...Array(50).keys()]) + .some(playerCount => playerCount in (MafiaData[`themes`][key]) && + (MafiaData[`themes`][key])[playerCount].toString().toLowerCase().includes(alias))); + } else if (searchType === `IDEAs` && alias in MafiaData[`roles`]) { + entries = entries.filter(([key, data]) => MafiaData[`IDEAs`][key].roles.map(role => + toID((toID(role) in MafiaData[`aliases`]) ? MafiaData[`aliases`][toID(role)] : role)).includes(alias)); + } else if (searchType === `roles` && alias in MafiaData[`themes`]) { + entries = entries.filter(([key, data]) => Object.keys(MafiaData[`themes`][alias]) + .filter((newKey: any) => toID((MafiaData[`themes`][alias])[newKey].toString()).includes(key)).length > 0); + } else if (searchType === `roles` && alias in MafiaData[`IDEAs`]) { + entries = entries.filter(([key, data]) => MafiaData[`IDEAs`][alias].roles.map(role => + toID((toID(role) in MafiaData[`aliases`]) ? MafiaData[`aliases`][toID(role)] : role)).includes(toID(key))); + } else { + entries = entries.filter(([key]) => Object.keys((MafiaData[searchType][key])) + .filter((newKey: keyof MafiaData[keyof MafiaData]) => (MafiaData[searchType][key])[newKey] + .toString().toLowerCase().includes(searchTarget)).length > 0); + } + return negation ? entriesCopy.filter(element => !entries.includes(element)) : entries; +} + // data assumptions - // the alignments "town" and "solo" always exist (defaults) // .alignment is always a valid key in data.alignments @@ -151,12 +208,16 @@ function writeFile(path: string, data: AnyObject) { MafiaData = readFile(DATA_FILE) || {alignments: {}, roles: {}, themes: {}, IDEAs: {}, terms: {}, aliases: {}}; if (!MafiaData.alignments.town) { MafiaData.alignments.town = { - name: 'town', plural: 'town', memo: [`This alignment is required for the script to function properly.`], + name: 'Town', + plural: 'Town', + memo: [`This alignment is required for the script to function properly.`], }; } if (!MafiaData.alignments.solo) { MafiaData.alignments.solo = { - name: 'solo', plural: 'solo', memo: [`This alignment is required for the script to function properly.`], + name: 'Solo', + plural: 'Solo', + memo: [`This alignment is required for the script to function properly.`], }; } @@ -323,6 +384,7 @@ class Mafia extends Rooms.RoomGame { dlAt: number; IDEA: MafiaIDEAModule; + constructor(room: ChatRoom, host: User) { super(room); @@ -335,6 +397,7 @@ class Mafia extends Rooms.RoomGame { this.hostid = host.id; this.host = Utils.escapeHTML(host.name); + this.cohostids = []; this.cohosts = []; @@ -2192,7 +2255,6 @@ export const commands: Chat.ChatCommands = { } return this.parse('/help mafia'); }, - forcehost: 'host', nexthost: 'host', host(target, room, user, connection, cmd) { @@ -4049,23 +4111,168 @@ export const commands: Chat.ChatCommands = { this.sendReply(`The entry ${entry} was deleted from the ${source} database.`); }, deletedatahelp: [`/mafia deletedata source,entry - Removes an entry from the database. Requires % @ # ~`], - listdata(target, room, user) { - if (!(target in MafiaData)) { - return this.errorReply(`Invalid source. Valid sources are ${Object.keys(MafiaData).join(', ')}`); + + randtheme: 'listdata', + randrole: 'listdata', + randalignment: 'listdata', + randidea: 'listdata', + randterm: 'listdata', + randroles: 'listdata', + randalignments: 'listdata', + randideas: 'listdata', + randterms: 'listdata', + randdata: 'listdata', + randomtheme: 'listdata', + randomrole: 'listdata', + randomalignment: 'listdata', + randomidea: 'listdata', + randomterm: 'listdata', + randomdata: 'listdata', + randomthemes: 'listdata', + randomroles: 'listdata', + randomalignments: 'listdata', + randomideas: 'listdata', + randomterms: 'listdata', + randthemes: 'listdata', + listthemes: 'listdata', + listroles: 'listdata', + listalignments: 'listdata', + listideas: 'listdata', + listterms: 'listdata', + themes: 'listdata', + roles: 'listdata', + alignments: 'listdata', + ideas: 'listdata', + terms: 'listdata', + ds: 'listdata', + search: 'listdata', + random: 'listdata', + list: 'listdata', + listdata(target, room, user, connection, cmd, message) { + if (!this.runBroadcast()) return false; + + // Determine non-search targets first, afterward searching is done with the remainder + const targets = target.split(',').map(x => x.trim().toLowerCase()); + + // Determine search type + let searchType: keyof MafiaData = 'aliases'; + if (cmd.includes('theme') || targets.includes(`themes`)) { + searchType = `themes`; + if (targets.includes(`themes`)) targets.splice(targets.indexOf(`themes`), 1); + } else if (cmd.includes('role') || targets.includes(`roles`)) { + searchType = `roles`; + if (targets.includes(`roles`)) targets.splice(targets.indexOf(`roles`), 1); + } else if (cmd.includes('alignment') || targets.includes(`alignments`)) { + searchType = `alignments`; + if (targets.includes(`alignments`)) targets.splice(targets.indexOf(`alignments`), 1); + } else if (cmd.includes('idea') || targets.includes(`ideas`)) { + searchType = `IDEAs`; + if (targets.includes(`ideas`)) targets.splice(targets.indexOf(`ideas`), 1); + } else if (cmd.includes('term') || targets.includes(`terms`)) { + searchType = `terms`; + if (targets.includes(`terms`)) targets.splice(targets.indexOf(`terms`), 1); + } else if (targets.includes(`aliases`)) { + searchType = `aliases`; + } else if (cmd === 'random' || cmd === 'randomdata' || cmd === 'randdata') { + searchType = + ([`themes`, `roles`, `alignments`, `IDEAs`, `terms`] as (keyof MafiaData)[])[Math.floor(Math.random() * 5)]; + } else { + return this.errorReply(`Invalid source. Valid sources are ${Object.keys(MafiaData).filter(key => key !== `aliases`).join(', ')}.`); + } + + const dataSource = MafiaData[searchType]; + + const random = (cmd.includes('rand') || targets.includes(`random`)); + + if (targets.includes(`random`)) targets.splice(targets.indexOf(`random`), 1); + + const hidden = (targets.includes(`hidden`)); + if (hidden) targets.splice(targets.indexOf(`hidden`), 1); + + const shuffle = function (array: any[]) { // replace by utils thing + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [array[j], array[i]] = [array[i], array[j]]; + } + return array; + }; + + // Number of results + let number = random ? 1 : 0; + for (let i = 0; i < targets.length; i++) { + if (((targets[i] !== null) && + (targets[i] !== '') && + !isNaN(Number(targets[i].toString())))) { + number = Number(targets[i]); + targets.splice(i, 1); + break; + } } - const dataSource = MafiaData[target as keyof MafiaData]; - if (dataSource === MafiaData.aliases) { + + // Convert to rows + const themeRow = function (theme: MafiaDataTheme, players = 0) { + return ` ${players > 0 ? theme[players] : theme.desc} `; + }; + const ideaRow = function (idea: MafiaDataIDEA) { + return ` `; + }; + const row = function (role: MafiaDataRole | MafiaDataTerm | MafiaDataAlignment) { + return ` ${role.memo.join(' ')} `; + }; + + if (searchType === `aliases`) { // Can I use dataSource > searchType + room = this.requireRoom(); + this.checkCan('mute', null, room); const aliases = Object.entries(MafiaData.aliases) .map(([from, to]) => `${from}: ${to}`) .join('
'); return this.sendReplyBox(`Mafia aliases:
${aliases}`); } else { - const entries = Object.entries(dataSource) - .map(([key, data]) => ``) + let table = `
`; + let entries: [string, MafiaDataAlignment | MafiaDataRole | MafiaDataTheme | MafiaDataIDEA | MafiaDataTerm][] = + Object.entries(dataSource).sort(); + + for (const targetString of targets) { + entries = targetString.split('|').map(x => x.trim()) + .map(searchTerm => mafiaSearch(entries.slice(), searchTerm, searchType)) + .reduce((aggregate, result) => [...new Set([...aggregate, ...result])]); + } + + if (typeof (entries) === 'undefined') return; + + if (random) entries = shuffle(entries); + if (number > 0) entries = entries.slice(0, number); + + if (entries.length === 0) { + return this.errorReply(`No ${searchType} found.`); + } + + if (entries.length === 1) { + this.target = entries[0][0]; + return this.run((Chat.commands.mafia as Chat.ChatCommands).data as Chat.AnnotatedChatHandler); + } + + table += entries + .map(([key, data]) => searchType === `themes` ? + themeRow(MafiaData[searchType][key]) : searchType === `IDEAs` ? + ideaRow(MafiaData[searchType][key]) : row(MafiaData[searchType as "roles" | "alignments" | "terms"][key as string])) .join(''); - return this.sendReplyBox(`Mafia ${target}:
${entries}`); + table += `
`; + return this.sendReplyBox(table); } }, + listdatahelp: [ + `/mafia roles [parameter, paramater, ...] - Views all Mafia roles. Parameters: theme that must include role, text included in role data.`, + `/mafia themes [parameter, paramater, ...] - Views all Mafia themes. Parameters: roles in theme, players(< | <= | = | => | >)[x] for playercounts, text included in theme data.`, + `/mafia alignments [parameter, paramater, ...] - Views all Mafia alignments. Parameters: text included in alignment data.`, + `/mafia ideas [parameter, paramater, ...] - Views all Mafia IDEAs. Parameters: roles in IDEA, text included in IDEA data.`, + `/mafia terms [parameter, paramater, ...] - Views all Mafia terms. Parameters: text included in term data.`, + `/mafia randomrole [parameter, paramater, ...] - View a random Mafia role. Parameters: number of roles to be randomly generated, theme that must include role, text included in role data.`, + `/mafia randomtheme [parameter, paramater, ...] - View a random Mafia theme. Parameters: number of themes to be randomly generated, roles in theme, players(< | <= | = | => | >)[x] for playercounts, text included in theme data.`, + `/mafia randomalignment [parameter, paramater, ...] - View a random Mafia alignment. Parameters: number of alignments to be randomly generated, text included in alignment data.`, + `/mafia randomidea [parameter, paramater, ...] - View a random Mafia IDEA. Parameters: number of IDEAs to be randomly generated, roles in IDEA, text included in IDEA data.`, + `/mafia randomterm [parameter, paramater, ...] - View a random Mafia term. Parameters: number of terms to be randomly generated, text included in term data.`, + ], disable(target, room, user) { room = this.requireRoom(); @@ -4198,6 +4405,22 @@ export const commands: Chat.ChatCommands = { `/mafia (un)gameban [user], [duration] - Ban a user from playing games for [duration] days. Requires % @ # ~`, ].join('
'); buf += ``; + buf += `
Mafia Dexsearch Commands`; + buf += [ + `
Commands to search Mafia data:
`, + `/mafia dt [data] - Views Mafia data.`, + `/mafia roles [parameter, paramater, ...] - Views all Mafia roles. Parameters: theme that must include role, text included in role data.`, + `/mafia themes [parameter, paramater, ...] - Views all Mafia themes. Parameters: roles in theme, players(< | <= | = | => | >)[x] for playercounts, text included in theme data.`, + `/mafia alignments [parameter, paramater, ...] - Views all Mafia alignments. Parameters: text included in alignment data.`, + `/mafia ideas [parameter, paramater, ...] - Views all Mafia IDEAs. Parameters: roles in IDEA, text included in IDEA data.`, + `/mafia terms [parameter, paramater, ...] - Views all Mafia terms. Parameters: text included in term data.`, + `/mafia randomrole [parameter, paramater, ...] - View a random Mafia role. Parameters: number of roles to be randomly generated, theme that must include role, text included in role data.`, + `/mafia randomtheme [parameter, paramater, ...] - View a random Mafia theme. Parameters: number of themes to be randomly generated, roles in theme, players(< | <= | = | => | >)[x] for playercounts, text included in theme data.`, + `/mafia randomalignment [parameter, paramater, ...] - View a random Mafia alignment. Parameters: number of alignments to be randomly generated, text included in alignment data.`, + `/mafia randomidea [parameter, paramater, ...] - View a random Mafia IDEA. Parameters: number of IDEAs to be randomly generated, roles in IDEA, text included in IDEA data.`, + `/mafia randomterm [parameter, paramater, ...] - View a random Mafia term. Parameters: number of terms to be randomly generated, text included in term data.`, + ].join('
'); + buf += `
`; return this.sendReplyBox(buf); }, From 2744d98d904a3c3126fcf24c2518ae6b3eebf721 Mon Sep 17 00:00:00 2001 From: "Meijer,L. (Lucas)" Date: Tue, 19 Nov 2024 22:43:41 +0100 Subject: [PATCH 2/2] Fix lambda function TypeScript errors --- server/chat-plugins/mafia.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/server/chat-plugins/mafia.ts b/server/chat-plugins/mafia.ts index 6eb3b5af2a27..550a713e2357 100644 --- a/server/chat-plugins/mafia.ts +++ b/server/chat-plugins/mafia.ts @@ -141,8 +141,10 @@ function writeFile(path: string, data: AnyObject) { )); } -function mafiaSearch(entries: [string, MafiaDataAlignment | MafiaDataRole | MafiaDataTheme | MafiaDataIDEA | MafiaDataTerm][], - searchTarget: string, searchType: keyof MafiaData) { +function mafiaSearch( + entries: [string, MafiaDataAlignment | MafiaDataRole | MafiaDataTheme | MafiaDataIDEA | MafiaDataTerm][], + searchTarget: string, searchType: keyof MafiaData +) { if (typeof (entries) === 'undefined') return entries; if (searchType === `aliases`) return entries; @@ -191,9 +193,8 @@ function mafiaSearch(entries: [string, MafiaDataAlignment | MafiaDataRole | Mafi entries = entries.filter(([key, data]) => MafiaData[`IDEAs`][alias].roles.map(role => toID((toID(role) in MafiaData[`aliases`]) ? MafiaData[`aliases`][toID(role)] : role)).includes(toID(key))); } else { - entries = entries.filter(([key]) => Object.keys((MafiaData[searchType][key])) - .filter((newKey: keyof MafiaData[keyof MafiaData]) => (MafiaData[searchType][key])[newKey] - .toString().toLowerCase().includes(searchTarget)).length > 0); + entries = entries.filter(([key]) => Object.entries(MafiaData[searchType][key]) + .some(([newKey, value]) => value.toString().toLowerCase().includes(searchTarget))); } return negation ? entriesCopy.filter(element => !entries.includes(element)) : entries; } @@ -4255,7 +4256,7 @@ export const commands: Chat.ChatCommands = { table += entries .map(([key, data]) => searchType === `themes` ? themeRow(MafiaData[searchType][key]) : searchType === `IDEAs` ? - ideaRow(MafiaData[searchType][key]) : row(MafiaData[searchType as "roles" | "alignments" | "terms"][key as string])) + ideaRow(MafiaData[searchType][key]) : row(MafiaData[searchType as "roles" | "alignments" | "terms"][key])) .join(''); table += ``; return this.sendReplyBox(table);