Convert to Typescript

This commit is contained in:
2024-09-16 09:56:40 +00:00
parent a89150fc2f
commit 6a59118f4a
42 changed files with 2916 additions and 785 deletions

224
src/commands/game.ts Normal file
View File

@@ -0,0 +1,224 @@
import {
SlashCommandBuilder,
EmbedBuilder,
ChatInputCommandInteraction,
} from 'discord.js';
import {
listGameNames,
getGame,
setGame,
deleteGame,
deleteField,
} from './utils/db/game';
// Initialise the command data.
export const data = new SlashCommandBuilder()
.setName('game')
.setDescription('Perform ButlerBot game database operations.')
.addSubcommand((subcommand) =>
subcommand.setName('list').setDescription('List all games in the database.')
)
.addSubcommand((subcommand) =>
subcommand
.setName('get')
.setDescription('Get a game from the database.')
.addStringOption((option) =>
option
.setName('game')
.setDescription('The name of the game to get.')
.setRequired(true)
)
)
.addSubcommand((subcommand) =>
subcommand
.setName('set')
.setDescription('Set a key value pair on a game in the database.')
.addStringOption((option) =>
option
.setName('game')
.setDescription('The name of the game to set the field in.')
.setRequired(true)
)
.addStringOption((option) =>
option
.setName('key')
.setDescription('The key or label to set.')
.setRequired(true)
)
.addStringOption((option) =>
option
.setName('value')
.setDescription('The value of the field to set.')
.setRequired(true)
.setMinLength(1)
.setMaxLength(1024)
)
)
.addSubcommand((subcommand) =>
subcommand
.setName('delete')
.setDescription('Delete a game, or a field from a game.')
.addStringOption((option) =>
option
.setName('game')
.setDescription(
'The name of the game to remove, or remove a field from.'
)
.setRequired(true)
)
.addStringOption((option) =>
option
.setName('key')
.setDescription('The key or label of the field to remove.')
.setRequired(false)
)
);
console.log(`Loaded ${data.name} command.`);
/**
* Handle the interaction for the game command.
* @param interaction The interaction that triggered the command.
* @returns A promise that resolves when the command is finished executing.
*/
export async function execute(
interaction: ChatInputCommandInteraction
): Promise<void> {
await interaction.deferReply();
const subcommand = interaction.options.getSubcommand();
switch (subcommand) {
case 'list': {
const gameNames = await listGameNames();
if (gameNames.length === 0) {
await interaction.editReply('No games found in the database.');
return;
}
const embed = new EmbedBuilder()
.setTitle('Game Database')
.setColor(0xff0000)
.setDescription('List of games in the database.');
embed.addFields({
name: 'Games',
value: gameNames.map((game) => game.game).join('\n'),
});
await interaction.editReply({ embeds: [embed] });
break;
}
case 'get': {
const gameName = interaction.options.getString('game', true);
const game = await getGame(gameName);
if (!game) {
await interaction.editReply(
`Game ${gameName} not found in the database.`
);
return;
}
try {
const embed = createEmbedFromGame(game);
await interaction.editReply({ embeds: [embed] });
} catch (error) {
await interaction.editReply(
'Embed too large to send. Game has not been retrieved.'
);
}
break;
}
case 'set': {
const gameName = interaction.options.getString('game', true);
const key = interaction.options.getString('key', true);
const value = interaction.options.getString('value', true);
const oldGame = await getGame(gameName);
await setGame(gameName, key, value);
const updatedGame = await getGame(gameName);
try {
const embed = createEmbedFromGame(updatedGame);
await interaction.editReply({ embeds: [embed] });
} catch (error) {
// Revert game back to original state if the embed is too large.
if (oldGame) {
// If key already existed, revert to old value, else delete the key.
if (oldGame[key]) {
await setGame(gameName, key, oldGame[key]);
} else {
await deleteField(gameName, key);
}
}
await interaction.editReply(
'Embed too large to send. Game has not been updated.'
);
}
break;
}
case 'delete': {
const gameName = interaction.options.getString('game', true);
const key = interaction.options.getString('key');
if (key) {
await deleteField(gameName, key);
const updatedGame = await getGame(gameName);
if (!updatedGame) {
await interaction.editReply(
`Game ${gameName} deleted from the database.`
);
return;
}
try {
const embed = createEmbedFromGame(updatedGame);
await interaction.editReply({ embeds: [embed] });
} catch (error) {
await interaction.editReply(
'Embed too large to send. Field deleted from game but game has not been retrieved.'
);
}
} else {
await deleteGame(gameName);
await interaction.editReply(
`Game ${gameName} deleted from the database.`
);
}
break;
}
default:
await interaction.editReply('Unknown subcommand.');
break;
}
}
/**
* Create an embed from a game object.
* @param game The game object containing key-value pairs.
* @returns The embed object to be sent in the interaction.
*/
function createEmbedFromGame(game: any): EmbedBuilder {
const embed = new EmbedBuilder().setTitle(game.game).setColor(0xff0000);
// Add fields for each key value pair - skip name, _id and guild.
embed.addFields(
Object.entries(game)
.filter(([key]) => !['_id', 'game', 'guild'].includes(key))
.map(([key, value]) => ({ name: key, value: String(value) }))
);
if (embed.length > 6000) {
throw new Error('Embed size exceeds maximum.');
}
return embed;
}