recipe creator FINISH

This commit is contained in:
Jill 2023-11-18 14:59:29 +03:00
parent 3da36de9f6
commit 3b9c66ebfd
Signed by: oat
GPG Key ID: 33489AA58A955108
6 changed files with 199 additions and 11 deletions

0
customCraftingRecipes Normal file
View File

View File

@ -17,7 +17,7 @@ rest
const commandFiles = fs.readdirSync("./dist/commands").filter((file) => file.endsWith(".js") && !file.startsWith('.'));
for (const file of commandFiles) {
const command = require(`./dist/commands/${file}`);
const command = require(`./dist/commands/${file}`).default;
commands.push(command);
}

View File

@ -4,12 +4,12 @@
*/
exports.up = function(knex) {
return knex.schema
.createTable('craftingRecipes', (table) => {
.createTable('customCraftingRecipes', (table) => {
table.increments('id');
table.string('table');
table.string('station');
})
.createTable('craftingRecipeItems', (table) => {
table.integer('id').references('id').inTable('craftingRecipes').notNullable();
.createTable('customCraftingRecipeItems', (table) => {
table.integer('id').references('id').inTable('customCraftingRecipes').notNullable();
table.integer('item').notNullable();
table.integer('quantity').defaultTo(1);
table.enum('type', ['input', 'output', 'requirement']);
@ -22,6 +22,6 @@ exports.up = function(knex) {
*/
exports.down = function(knex) {
return knex.schema
.dropTable('craftingRecipes')
.dropTable('craftingRecipeItems');
.dropTable('customCraftingRecipes')
.dropTable('customCraftingRecipes');
};

View File

@ -1,5 +1,9 @@
import { CommandInteraction, SlashCommandBuilder } from 'discord.js';
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, ComponentType, Events, ModalBuilder, SlashCommandBuilder, StringSelectMenuBuilder, TextInputBuilder, TextInputStyle } from 'discord.js';
import { Command } from '../types/index';
import { Items, getItem } from '../lib/rpg/items';
import { formatRecipe } from '../lib/rpg/recipes';
import { craftingStations, getStation } from '../lib/rpg/craftingStations';
import { CustomCraftingRecipe, CustomCraftingRecipeItem, db } from '../lib/db';
export default {
data: new SlashCommandBuilder()
@ -16,15 +20,188 @@ export default {
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
interaction.deferReply({ ephemeral: true });
await interaction.deferReply({ ephemeral: true });
const sub = interaction.options.getSubcommand(true);
if (sub === 'create') {
interaction.reply({
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder().setCustomId(`recipe-create-${interaction.guildId}`).setLabel('I\'ve got my string ready!').setStyle(ButtonStyle.Primary)
);
await interaction.followUp({
ephemeral: true,
content: `To create a recipe, go here: ${interaction.client.config.siteURL}/create-recipe/?guild=${interaction.guildId}\nOnce done, click the button below and paste the resulting string in.`
content: `To create a recipe, go here: ${interaction.client.config.siteURL}/create-recipe/?guild=${interaction.guildId}\nOnce done, click the button below and paste the resulting string in.`,
components: [row]
});
}
},
async onClientReady(bot) {
bot.on(Events.InteractionCreate, async (interaction) => {
if (!('customId' in interaction)) return;
const id = interaction.customId;
if (!id.startsWith('recipe-')) return;
if (!interaction.member) return;
if (id.startsWith('recipe-create-')) {
//const guildID = id.split('-')[2];
if (interaction.isMessageComponent()) {
const modal = new ModalBuilder()
.setCustomId(interaction.customId)
.setTitle('Recipe Creator');
const input = new TextInputBuilder()
.setCustomId('recipe-create-textbox')
.setLabel('Paste in your recipe string here:')
.setStyle(TextInputStyle.Paragraph)
.setRequired(true);
const row = new ActionRowBuilder<TextInputBuilder>().addComponents(input);
modal.addComponents(row);
interaction.showModal(modal);
} else if (interaction.isModalSubmit()) {
const field = interaction.fields.getField('recipe-create-textbox', ComponentType.TextInput);
const recipeString = field.value;
await interaction.deferReply({ ephemeral: true });
let parsed;
try {
parsed = await Promise.all(
recipeString
.split('|')
.map(items =>
Promise.all(
items
.split(';')
.map(itemStack =>
itemStack.split(',')
)
.map(async ([itemID, quantity]) => (
{
item: (await getItem(parseInt(itemID)))!,
quantity: parseInt(quantity)
}
))
)
)
) as Items[][];
} catch (err) {
await interaction.followUp(`This is not a valid string!: \`${(err as Error).message}\``);
return;
}
const
inputs = parsed[0] || [],
requirements = parsed[1] || [],
outputs = parsed[2] || [];
const recipe = {
inputs, requirements, outputs,
station: 'hands',
id: 0
};
const components = [
new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
new StringSelectMenuBuilder()
.addOptions(
...craftingStations
.map(station => ({
label: `${station.emoji} ${station.name}`,
value: station.key,
description: station.description
}))
)
.setMinValues(1)
.setMaxValues(1)
.setCustomId('recipe-select-station')
),
new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setCustomId('recipe-select-done')
.setLabel('Done')
.setStyle(ButtonStyle.Primary)
.setDisabled(true)
)
];
const msg = await interaction.followUp({
content: `${formatRecipe(recipe)}\n_Select a crafting station, and you're good to go!_`,
components: components,
fetchReply: true
});
const selectCollector = msg.createMessageComponentCollector({
componentType: ComponentType.StringSelect,
time: 60_000 * 5,
});
selectCollector.on('collect', selectInteraction => {
const newStation = selectInteraction.values[0];
recipe.station = newStation;
components[1].components[0].setDisabled(false);
interaction.editReply({
content: `${formatRecipe(recipe)}\n_Select a crafting station, and you're good to go!_`,
components: components
});
const station = getStation(newStation);
selectInteraction.reply({
content: `Set station to ${station?.emoji} **${station?.name}**`,
ephemeral: true
});
});
selectCollector.on('end', () => {
interaction.editReply({
content: msg.content,
components: []
});
});
const buttonInteraction = await msg.awaitMessageComponent({ componentType: ComponentType.Button, time: 60_000 * 5 });
selectCollector.stop();
const [customRecipe] = await db<CustomCraftingRecipe>('customCraftingRecipes')
.insert({
station: recipe.station
})
.returning('id');
for (const input of recipe.inputs) {
await db<CustomCraftingRecipeItem>('customCraftingRecipeItems')
.insert({
id: customRecipe.id,
item: input.item.id,
quantity: input.quantity,
type: 'input'
});
}
for (const req of recipe.requirements) {
await db<CustomCraftingRecipeItem>('customCraftingRecipeItems')
.insert({
id: customRecipe.id,
item: req.item.id,
quantity: req.quantity,
type: 'requirement'
});
}
for (const output of recipe.outputs) {
await db<CustomCraftingRecipeItem>('customCraftingRecipeItems')
.insert({
id: customRecipe.id,
item: output.item.id,
quantity: output.quantity,
type: 'output'
});
}
buttonInteraction.reply({
ephemeral: true,
content: 'Your recipe has been created 🎉'
});
}
}
});
}
} satisfies Command;

View File

@ -733,6 +733,7 @@ export default {
});
bot.on(Events.InteractionCreate, interaction => {
if (!interaction.isModalSubmit()) return;
if (!interaction.customId.startsWith('survey-')) return;
if (!interaction.member) return;
const member = interaction.member as GuildMember;

View File

@ -68,4 +68,14 @@ export interface CraftingStationCooldown {
station: string,
user: string,
usedAt: number
}
export interface CustomCraftingRecipe {
id: number,
station: string
}
export interface CustomCraftingRecipeItem {
id: number,
item: number,
quantity: number,
type: 'input' | 'output' | 'requirement'
}