225 lines
6.2 KiB
TypeScript
225 lines
6.2 KiB
TypeScript
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;
|
|
}
|