174 lines
5.7 KiB
TypeScript
174 lines
5.7 KiB
TypeScript
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<void>) {
|
|
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<string | null> {
|
|
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<string> {
|
|
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']
|
|
}
|
|
})
|
|
));
|
|
} |