import { createCommand } from "@shared/lib/utils"; import { SlashCommandBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, type BaseGuildTextChannel, PermissionFlagsBits, MessageFlags } from "discord.js"; import { inventoryService } from "@shared/modules/inventory/inventory.service"; import { createSuccessEmbed, createErrorEmbed, createBaseEmbed } from "@lib/embeds"; import { UserError } from "@/lib/errors"; import { items } from "@db/schema"; import { ilike, isNotNull, and } from "drizzle-orm"; import { DrizzleClient } from "@shared/db/DrizzleClient"; import { getShopListingMessage } from "@/modules/economy/shop.view"; export const listing = createCommand({ data: new SlashCommandBuilder() .setName("listing") .setDescription("Post an item listing in the channel for users to buy") .addNumberOption(option => option.setName("item") .setDescription("The item to list") .setRequired(true) .setAutocomplete(true) ) .addChannelOption(option => option.setName("channel") .setDescription("The channel to post the listing in (defaults to current)") .setRequired(false) ) .setDefaultMemberPermissions(PermissionFlagsBits.Administrator), execute: async (interaction) => { await interaction.deferReply({ flags: MessageFlags.Ephemeral }); const itemId = interaction.options.getNumber("item", 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: [createErrorEmbed(`Item "${item.name}" is not for sale (no price set).`)] }); return; } const listingMessage = getShopListingMessage({ ...item, formattedPrice: `${item.price} 🪙`, price: item.price }); try { await targetChannel.send(listingMessage); await interaction.editReply({ content: `✅ Listing for **${item.name}** posted in ${targetChannel}.` }); } catch (error: any) { if (error instanceof UserError) { await interaction.reply({ embeds: [createErrorEmbed(error.message)], ephemeral: true }); } else { console.error("Error creating listing:", error); await interaction.reply({ embeds: [createErrorEmbed("An unexpected error occurred.")], ephemeral: true }); } } }, autocomplete: async (interaction) => { const focusedValue = interaction.options.getFocused(); const results = await DrizzleClient.select({ id: items.id, name: items.name, price: items.price }) .from(items) .where( and( ilike(items.name, `%${focusedValue}%`), isNotNull(items.price) ) ) .limit(20); await interaction.respond( results.map(item => ({ name: `${item.name} (Price: ${item.price})`, value: item.id })) ); } });