From db4e7313c393ea75a2b563986d520ec9b9cf422a Mon Sep 17 00:00:00 2001 From: syntaxbullet Date: Fri, 6 Feb 2026 12:52:15 +0100 Subject: [PATCH] feat: Add support for local asset URLs for shop item icons and images, attaching them to Discord messages. --- bot/modules/economy/shop.view.ts | 50 +++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/bot/modules/economy/shop.view.ts b/bot/modules/economy/shop.view.ts index a50271b..f5e74af 100644 --- a/bot/modules/economy/shop.view.ts +++ b/bot/modules/economy/shop.view.ts @@ -1,16 +1,46 @@ -import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, AttachmentBuilder } from "discord.js"; import { createBaseEmbed } from "@/lib/embeds"; -import { resolveAssetUrl } from "@shared/lib/assets"; +import { resolveAssetUrl, isLocalAssetUrl } from "@shared/lib/assets"; +import { join } from "path"; +import { existsSync } from "fs"; export function getShopListingMessage(item: { id: number; name: string; description: string | null; formattedPrice: string; iconUrl: string | null; imageUrl: string | null; price: number | bigint }) { - // Resolve asset URLs to full URLs for Discord embeds - const resolvedIconUrl = resolveAssetUrl(item.iconUrl); - const resolvedImageUrl = resolveAssetUrl(item.imageUrl); + const files: AttachmentBuilder[] = []; + let thumbnailUrl = resolveAssetUrl(item.iconUrl); + let displayImageUrl = resolveAssetUrl(item.imageUrl); + + // Handle local icon + if (item.iconUrl && isLocalAssetUrl(item.iconUrl)) { + const iconPath = join(process.cwd(), "bot/assets/graphics", item.iconUrl.replace(/^\/?assets\//, "")); + if (existsSync(iconPath)) { + const iconName = defaultName(item.iconUrl); + files.push(new AttachmentBuilder(iconPath, { name: iconName })); + thumbnailUrl = `attachment://${iconName}`; + } + } + + // Handle local image (avoid duplicate attachments if same as icon) + if (item.imageUrl && isLocalAssetUrl(item.imageUrl)) { + // If image is same as icon, just use the same attachment reference + if (item.imageUrl === item.iconUrl && thumbnailUrl?.startsWith("attachment://")) { + displayImageUrl = thumbnailUrl; + } else { + const imagePath = join(process.cwd(), "bot/assets/graphics", item.imageUrl.replace(/^\/?assets\//, "")); + if (existsSync(imagePath)) { + const imageName = defaultName(item.imageUrl); + // Check if we already attached this file (by name) + if (!files.find(f => f.name === imageName)) { + files.push(new AttachmentBuilder(imagePath, { name: imageName })); + } + displayImageUrl = `attachment://${imageName}`; + } + } + } const embed = createBaseEmbed(`Shop: ${item.name}`, item.description || "No description available.", "Green") .addFields({ name: "Price", value: item.formattedPrice, inline: true }) - .setThumbnail(resolvedIconUrl) - .setImage(resolvedImageUrl) + .setThumbnail(thumbnailUrl) + .setImage(displayImageUrl) .setFooter({ text: "Click the button below to purchase instantly." }); const buyButton = new ButtonBuilder() @@ -21,5 +51,9 @@ export function getShopListingMessage(item: { id: number; name: string; descript const row = new ActionRowBuilder().addComponents(buyButton); - return { embeds: [embed], components: [row] }; + return { embeds: [embed], components: [row], files }; +} + +function defaultName(path: string): string { + return path.split("/").pop() || "image.png"; }