import { AutocompleteInteraction, CommandInteraction, SlashCommandBuilder } from 'discord.js'; import { Counter, CustomCraftingRecipeItem, CustomItem, db } from '../lib/db'; import { customItemAutocomplete, formatItem, formatItems, getItem, giveItem, itemAutocomplete } from '../lib/rpg/items'; import { Command } from '../types/index'; import { formatRecipe, getCustomRecipe } from '../lib/rpg/recipes'; //function extendOption(t: string) { // return {name: t, value: t}; //} export default { data: new SlashCommandBuilder() .setName('item') .setDescription('[ADMIN] Create, edit and otherwise deal with custom items') .addSubcommandGroup(grp => grp .setName('add') .setDescription('[ADMIN] Create an item') .addSubcommand(cmd => cmd .setName('plain') .setDescription('A normal, functionless item') .addStringOption(opt => opt .setName('name') .setDescription('The item name') .setRequired(true) ) .addStringOption(opt => opt .setName('emoji') .setDescription('An emoji or symbol that could represent this item') .setRequired(true) ) .addStringOption(opt => opt .setName('description') .setDescription('A short description') ) .addIntegerOption(opt => opt .setName('maxstack') .setDescription('Maximum amount of this item you\'re able to hold at once') ) .addBooleanOption(opt => opt .setName('untradable') .setDescription('Can you give this item to other people?') ) ) .addSubcommand(cmd => cmd .setName('weapon') .setDescription('A weapon that you can attack things with') .addStringOption(opt => opt .setName('name') .setDescription('The item name') .setRequired(true) ) .addStringOption(opt => opt .setName('emoji') .setDescription('An emoji or symbol that could represent this item') .setRequired(true) ) .addIntegerOption(opt => opt .setName('damage') .setDescription('How much base damage this weapon is intended to deal') .setRequired(true) ) .addStringOption(opt => opt .setName('description') .setDescription('A short description') ) .addBooleanOption(opt => opt .setName('untradable') .setDescription('Can you give this item to other people?') ) ) .addSubcommand(cmd => cmd .setName('consumable') .setDescription('Consumable item, usable once and never again') .addStringOption(opt => opt .setName('name') .setDescription('The item name') .setRequired(true) ) .addStringOption(opt => opt .setName('emoji') .setDescription('An emoji or symbol that could represent this item') .setRequired(true) ) .addStringOption(opt => opt .setName('description') .setDescription('A short description') ) .addIntegerOption(opt => opt .setName('maxstack') .setDescription('Maximum amount of this item you\'re able to hold at once') ) .addBooleanOption(opt => opt .setName('untradable') .setDescription('Can you give this item to other people?') ) ) ) .addSubcommand(cmd => cmd .setName('give') .setDescription('[ADMIN] Give a user an item') .addUserOption(opt => opt .setName('who') .setDescription('The user') .setRequired(true) ) .addStringOption(opt => opt .setName('item') .setDescription('The item') .setAutocomplete(true) .setRequired(true) ) .addIntegerOption(opt => opt .setName('quantity') .setDescription('Amount of items to give') ) ) .addSubcommand(cmd => cmd .setName('delete') .setDescription('[ADMIN] Delete a custom item') .addStringOption(opt => opt .setName('customitem') .setDescription('The item') .setAutocomplete(true) .setRequired(true) ) ) .setDefaultMemberPermissions('0') .setDMPermission(false), execute: async (interaction: CommandInteraction) => { if (!interaction.isChatInputCommand()) return; await interaction.deferReply({ephemeral: true}); const subcommand = interaction.options.getSubcommand(true); const group = interaction.options.getSubcommandGroup(); if (group === 'add') { const item = await db('customItems') .insert({ 'guild': interaction.guildId!, 'name': interaction.options.getString('name', true).trim(), 'description': interaction.options.getString('description') || undefined, 'emoji': interaction.options.getString('emoji', true).trim(), 'type': subcommand as 'plain' | 'weapon' | 'consumable', // kind of wild that ts makes you do this 'maxStack': (interaction.options.getInteger('maxstack') || interaction.options.getInteger('damage')) || (subcommand === 'weapon' ? 1 : 64), 'untradable': interaction.options.getBoolean('untradable') || false, }) .returning('*'); await interaction.followUp(`${JSON.stringify(item[0])}`); } else { if (subcommand === 'give') { const user = interaction.options.getUser('who', true); const itemID = parseInt(interaction.options.getString('item', true)); const quantity = interaction.options.getInteger('quantity') || 1; const item = await getItem(itemID); if (!item) return interaction.followUp('No such item exists!'); const inv = await giveItem(user.id, item, quantity); await interaction.followUp(`${user.toString()} now has ${formatItems(item, inv.quantity)}.`); } else if (subcommand === 'delete') { const itemID = parseInt(interaction.options.getString('customitem', true)); const item = await getItem(itemID); if (!item) return interaction.followUp('No such item exists!'); const usedIn = await db('customCraftingRecipeItems') .where('item', item.id); if (usedIn.length > 0) { const recipes = (await Promise.all(usedIn.map(i => getCustomRecipe(i.id)))).filter(r => r !== undefined); return interaction.followUp(`⚠️ This item is used in the following recipes:\n${recipes.map(r => `- ${formatRecipe(r!)}`).join('\n')}`); } const linkedWith = await db('counters') .where('linkedItem', item.id); if (linkedWith.length > 0) { return interaction.followUp(`⚠️ This item is used in the following counters:\n${linkedWith.map(c => `- ${c.key} ${c.value} in <#${c.channel}>`).join('\n')}`); } await db('customItems') .where('id', item.id) .delete(); interaction.followUp(`${formatItem(item)} has been deleted.`); } } }, autocomplete: async (interaction: AutocompleteInteraction) => { const focused = interaction.options.getFocused(true); if (focused.name === 'item') { return itemAutocomplete(interaction); } else if (focused.name === 'customitem') { return customItemAutocomplete(interaction); } } } satisfies Command;