import { createCommand } from "@/lib/utils"; import { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, ComponentType, type BaseGuildTextChannel, type ButtonInteraction, PermissionFlagsBits, MessageFlags } from "discord.js"; import { userService } from "@/modules/user/user.service"; import { inventoryService } from "@/modules/inventory/inventory.service"; import type { items } from "@db/schema"; import { createErrorEmbed, createWarningEmbed } from "@lib/embeds"; export const sell = createCommand({ data: new SlashCommandBuilder() .setName("sell") .setDescription("Post an item for sale in the current channel so regular users can buy it") .addNumberOption(option => option.setName("itemid") .setDescription("The ID of the item to sell") .setRequired(true) ) .addChannelOption(option => option.setName("channel") .setDescription("The channel to post the item in") .setRequired(false) ) .setDefaultMemberPermissions(PermissionFlagsBits.Administrator), execute: async (interaction) => { await interaction.deferReply({ flags: MessageFlags.Ephemeral }); const itemId = interaction.options.getNumber("itemid", true); const targetChannel = (interaction.options.getChannel("channel") as BaseGuildTextChannel) || interaction.channel as BaseGuildTextChannel; if (!targetChannel || !targetChannel.isSendable()) { await interaction.editReply({ content: "", embeds: [createErrorEmbed("Target channel is invalid or not sendable.")] }); return; } const item = await inventoryService.getItem(itemId); if (!item) { await interaction.editReply({ content: "", embeds: [createErrorEmbed(`Item with ID ${itemId} not found.`)] }); return; } if (!item.price) { await interaction.editReply({ content: "", embeds: [createWarningEmbed(`Item "${item.name}" is not for sale (no price set).`)] }); return; } const embed = new EmbedBuilder() .setTitle(`Item for sale: ${item.name}`) .setDescription(item.description || "No description available.") .addFields({ name: "Price", value: `${item.price} 🪙`, inline: true }) .setColor("Yellow") .setThumbnail(item.iconUrl || null) .setImage(item.imageUrl || null); const buyButton = new ButtonBuilder() .setCustomId("buy") .setLabel("Buy") .setStyle(ButtonStyle.Success); const actionRow = new ActionRowBuilder().addComponents(buyButton); try { const message = await targetChannel.send({ embeds: [embed], components: [actionRow] }); await interaction.editReply({ content: `Item posted in ${targetChannel}.` }); // Create a collector on the specific message const collector = message.createMessageComponentCollector({ componentType: ComponentType.Button, filter: (i) => i.customId === "buy", }); collector.on("collect", async (i) => { await handleBuyInteraction(i, item); }); } catch (error) { console.error("Failed to send sell message:", error); await interaction.editReply({ content: "", embeds: [createErrorEmbed("Failed to post the item for sale.")] }); } } }); async function handleBuyInteraction(interaction: ButtonInteraction, item: typeof items.$inferSelect) { try { await interaction.deferReply({ flags: MessageFlags.Ephemeral }); const userId = interaction.user.id; const user = await userService.getUserById(userId); if (!user) { await interaction.editReply({ content: "", embeds: [createErrorEmbed("User profile not found.")] }); return; } if ((user.balance ?? 0n) < (item.price ?? 0n)) { await interaction.editReply({ content: "", embeds: [createWarningEmbed(`You don't have enough money! You need ${item.price} 🪙.`)] }); return; } const result = await inventoryService.buyItem(userId, item.id, 1n); if (!result.success) { await interaction.editReply({ content: "", embeds: [createErrorEmbed("Transaction failed. Please try again.")] }); return; } await interaction.editReply({ content: `Successfully bought **${item.name}** for ${item.price} 🪙!` }); } catch (error) { console.error("Error processing purchase:", error); if (interaction.deferred || interaction.replied) { await interaction.editReply({ content: "", embeds: [createErrorEmbed("An error occurred while processing your purchase.")] }); } else { await interaction.reply({ embeds: [createErrorEmbed("An error occurred while processing your purchase.")], flags: MessageFlags.Ephemeral }); } } }