import { Interaction, Message, TextBasedChannel, User } from 'discord.js'; import * as log from '../lib/log'; import { chunks } from './util'; export const RANDOM_WORDS = [ 'tarsorado', 'aboba', 'radiation', 'extreme', 'glogging', 'glogged', 'penis', 'easy', 'glue', 'contaminated water', 'centrifuge', 'plutonium', 'uranium', 'thorium', 'imposter', 'đŸ’Ĩ', 'đŸĨĩ', '🎊', '!!!', '...', '???', '?..', '?!', '!', '?', 'balls itch', 'gas leak', 'among us', 'overclock', 'mandelbrot', 'novosibirsk', 'oops!', 'memory leak', 'pepsi can' ]; export function randomWord() { return RANDOM_WORDS[Math.floor(Math.random() * RANDOM_WORDS.length)]; } const DONE_EMOJI = '👍'; const BAD_EMOJI = '👎'; const DEFAULT_EMOJI = 'đŸĒ™'; const STOP_EMOJI = '⏚ī¸'; const CANCEL_EMOJI = '❌'; function formatMessage(users: User[], time: number, name: string, ended = false, cancelled = false) { return `Starting a **${name}** game (${users.length} player${users.length !== 1 ? 's' : ''})\n` + users.map(user => `- ${user.toString()}`).join('\n') + '\n' + (time <= 0 ? (cancelled ? ('**Game was cancelled**') : (ended ? '**Already ended!**' : '**Already started**')) : `Starting in **${Math.ceil(time / 1000)}s** - react ${STOP_EMOJI} to begin now` ); } let membersInGame: string[] = []; export async function startGame(interaction: Interaction, startingUser: User, name: string, callback: (players: User[], channel: TextBasedChannel) => Promise) { if (!interaction.isChatInputCommand()) return; if (membersInGame.includes(startingUser.id)) { await interaction.reply({ ephemeral: true, content: 'You are already in a game!' }); return; } membersInGame.push(startingUser.id); let participants: User[] = [startingUser]; const duration = 25_000; const m = await interaction.reply({ fetchReply: true, content: formatMessage(participants, duration, name) }); if (!(m instanceof Message)) return; const emoji = m.guild?.emojis.cache.random(); const started = Date.now(); const collector = m.createReactionCollector({ filter: (reaction, user) => !user.bot && ( ( (emoji ? reaction.emoji.id === emoji.id : reaction.emoji.name === DEFAULT_EMOJI) && !participants.find(u => user.id === u.id) && !membersInGame.includes(user.id) ) || (reaction.emoji.name === STOP_EMOJI && user.id === startingUser.id) || (reaction.emoji.name === CANCEL_EMOJI && user.id === startingUser.id) ), time: duration, dispose: true }); await Promise.all([ m.react(emoji || DEFAULT_EMOJI), m.react(STOP_EMOJI), m.react(CANCEL_EMOJI), ]); const updateInterval = setInterval(() => { m.edit(formatMessage(participants, duration - (Date.now() - started), name)); }, 3_000); collector.on('collect', (reaction, user) => { if (reaction.emoji.name === STOP_EMOJI) { collector.stop('force-started'); } else if (reaction.emoji.name === CANCEL_EMOJI) { collector.stop('cancelled'); } else { participants.push(user); membersInGame.push(user.id); m.edit(formatMessage(participants, duration - (Date.now() - started), name)); } }); collector.on('remove', (_, user) => { participants = participants.filter(u => u.id !== user.id); membersInGame = membersInGame.filter(u => u !== user.id); m.edit(formatMessage(participants, duration - (Date.now() - started), name)); }); collector.on('end', async (_, reason) => { clearInterval(updateInterval); await m.reactions.removeAll().catch(error => log.error(error)); if (reason === 'cancelled') { m.edit(formatMessage(participants, 0, name, false, true)); } else { m.edit(formatMessage(participants, 0, name)); await callback(participants, m.channel); m.edit(formatMessage(participants, 0, name, true)); } membersInGame = membersInGame.filter(id => !participants.find(u => u.id === id)); }); } export async function getTextResponse(user: User, prompt: string, filter: (content: string) => boolean = () => true): Promise { const msg = await user.send(prompt); try { const collected = await msg.channel.awaitMessages({ max: 1, time: 45_000, errors: ['time'], filter: (msg) => { const valid = msg.content !== '' && msg.content.length <= 2000 && filter(msg.content); if (!valid) msg.react(BAD_EMOJI); return valid; } }); const message = collected.first() as Message; await message.react(DONE_EMOJI); return message.content; } catch (err) { return null; } } export async function getTextResponsePrettyPlease(user: User, prompt: string, filter: (content: string) => boolean = () => true): Promise { const resp = await getTextResponse(user, prompt, filter); if (resp) return resp; user.send('Took too long... Surprise... Added...... :)'); return randomWord(); } export async function sendSegments(segments: string[], channel: TextBasedChannel) { const content = []; let contentBuffer = ''; while (segments.length > 0) { const segment = segments.splice(0, 1)[0]; const newMsg = contentBuffer + '\n' + segment; if (newMsg.length > 2000) { content.push(contentBuffer); contentBuffer = ''; if (segment.length > 2000) { content.push(...([...(chunks(segment.split(''), 2000))].map(s => s.join('')))); } else { contentBuffer = segment; } } else { contentBuffer = newMsg; } } if (contentBuffer !== '') content.push(contentBuffer); return Promise.all(content.map(async content => await channel.send({ content: content, allowedMentions: { parse: ['users'] } }) )); }