import { Utils, FS } from '../../lib'; export const nameList = new Set(JSON.parse( FS('config/chat-plugins/usersearch.json').readIfExistsSync() || "[]" )); const ONLINE_SYMBOL = ` \u25C9 `; const OFFLINE_SYMBOL = ` \u25CC `; class PunishmentHTML extends Chat.JSX.Component<{ userid: ID, target: string }> { render() { const { userid, target } = { ...this.props }; const buf = []; for (const cmdName of ['Forcerename', 'Namelock', 'Weeknamelock']) { // We have to use dangerouslySetInnerHTML here because otherwise the `value` // property of the button tag is auto escaped, making into &#10; buf.push(${cmdName}`, }} />); } return buf; } } class SearchUsernames extends Chat.JSX.Component<{ target: string, page?: boolean }> { render() { const { target, page } = { ...this.props }; const results: { offline: string[], online: string[] } = { offline: [], online: [], }; for (const curUser of Users.users.values()) { if (!curUser.id.includes(target) || curUser.id.startsWith('guest')) continue; if (Punishments.isGlobalBanned(curUser)) continue; if (curUser.connected) { results.online.push(`${!page ? ONLINE_SYMBOL : ''} ${curUser.name}`); } else { results.offline.push(`${!page ? OFFLINE_SYMBOL : ''} ${curUser.name}`); } } for (const k in results) { Utils.sortBy(results[k as keyof typeof results], result => toID(result)); } if (!page) { return <> Users with a name matching '{target}':
{!results.offline.length && !results.online.length ? ( <>No users found. ) : ( <> {results.online.join('; ')} {!!results.offline.length && <>{!!results.online.length && <>

}{results.offline.join('; ')}} )} ; } return

Usernames containing "{target}"

{!results.online.length && !results.offline.length ? (

No results found.

) : ( <>{!!results.online.length &&

Online users

{(() => { const online = []; for (const username of results.online) { online.push(); } return online; })()}
Username Punish
{username}
} {!!(results.online.length && results.offline.length) &&
} {!!results.offline.length &&

Offline users

{(() => { const offline = []; for (const username of results.offline) { offline.push(); } return offline; })()}
Username Punish
{username}
} )}
; } } function saveNames() { FS('config/chat-plugins/usersearch.json').writeUpdate(() => JSON.stringify([...nameList])); } export const commands: Chat.ChatCommands = { us: 'usersearch', uspage: 'usersearch', usersearchpage: 'usersearch', usersearch(target, room, user, connection, cmd) { this.checkCan('lock'); target = toID(target); if (!target) { // just join directly if it's the page cmd, they're likely looking for the full list if (cmd.includes('page')) return this.parse(`/j view-usersearch`); return this.parse(`/help usersearch`); } if (target.length < 3) { throw new Chat.ErrorMessage(`That's too short of a term to search for.`); } const showPage = cmd.includes('page'); if (showPage) { this.parse(`/j view-usersearch-${target}`); return; } return this.sendReplyBox(); }, usersearchhelp: [ `/usersearch [pattern]: Looks for all names matching the [pattern]. Requires: % @ ~`, `Adding "page" to the end of the command, i.e. /usersearchpage OR /uspage will bring up a page.`, `See also /usnames for a staff-curated list of the most commonly searched terms.`, ], usnames: 'usersearchnames', usersearchnames: { '': 'list', list() { this.parse(`/join view-usersearch`); }, add(target, room, user) { this.checkCan('lock'); const targets = target.split(',').map(toID).filter(Boolean); if (!targets.length) { return this.errorReply(`Specify at least one term.`); } for (const [i, arg] of targets.entries()) { if (nameList.has(arg)) { targets.splice(i, 1); this.errorReply(`Term ${arg} is already on the usersearch term list.`); continue; } if (arg.length < 3) { targets.splice(i, 1); this.errorReply(`Term ${arg} is too short for the usersearch term list. Must be more than 3 characters.`); continue; } nameList.add(arg); } if (!targets.length) { // fuck you too, "mia added 0 term to the usersearch name list" return this.errorReply(`No terms could be added.`); } const count = Chat.count(targets, 'terms'); Rooms.get('staff')?.addByUser( user, `${user.name} added the ${count} "${targets.join(', ')}" to the usersearch name list.` ); this.globalModlog(`USERSEARCH ADD`, null, targets.join(', ')); if (!room || room.roomid !== 'staff') { this.sendReply(`You added the ${count} "${targets.join(', ')}" to the usersearch name list.`); } saveNames(); }, remove(target, room, user) { this.checkCan('lock'); const targets = target.split(',').map(toID).filter(Boolean); if (!targets.length) { return this.errorReply(`Specify at least one term.`); } for (const [i, arg] of targets.entries()) { if (!nameList.has(arg)) { targets.splice(i, 1); this.errorReply(`${arg} is not in the usersearch name list, and has been skipped.`); continue; } nameList.delete(arg); } if (!targets.length) { return this.errorReply(`No terms could be removed.`); } const count = Chat.count(targets, 'terms'); Rooms.get('staff')?.addByUser( user, `${user.name} removed the ${count} "${targets.join(', ')}" from the usersearch name list.` ); this.globalModlog(`USERSEARCH REMOVE`, null, targets.join(', ')); if (!room || room.roomid !== 'staff') { this.sendReply(`You removed the ${count} "${targets.join(', ')}"" from the usersearch name list.`); } saveNames(); }, }, usnameshelp: [ `/usnames add [...terms]: Adds the given [terms] to the usersearch name list. Requires: % @ ~`, `/usnames remove [...terms]: Removes the given [terms] from the usersearch name list. Requires: % @ ~`, `/usnames OR /usnames list: Shows the usersearch name list.`, ], }; export const pages: Chat.PageTable = { usersearch(query, user) { this.checkCan('lock'); const target = toID(query.shift()); if (!target) { this.title = `[Usersearch Terms]`; const sorted: { [k: string]: number } = {}; for (const curUser of Users.users.values()) { for (const term of nameList) { if (curUser.id.includes(term)) { if (!(term in sorted)) sorted[term] = 0; sorted[term]++; } } } return
Usersearch term list
{!nameList.size ? (

None found.

) : (
{(() => { const buf = []; for (const k of Utils.sortBy(Object.keys(sorted), v => -sorted[v])) { buf.push(); } if (!buf.length) return ; return buf; })()}
Term Current Matches
{k} {sorted[k]}
No names found.
)}
; } this.title = `[Usersearch] ${target}`; return ; }, };