Update shop command part1
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import type { CommandMessage } from '../../../core/types/commands';
|
import type { CommandMessage } from "../../../core/types/commands";
|
||||||
import type Amayo from '../../../core/client';
|
import type Amayo from "../../../core/client";
|
||||||
import {
|
import {
|
||||||
Message,
|
Message,
|
||||||
ButtonInteraction,
|
ButtonInteraction,
|
||||||
@@ -7,18 +7,18 @@ import {
|
|||||||
ComponentType,
|
ComponentType,
|
||||||
ButtonStyle,
|
ButtonStyle,
|
||||||
MessageFlags,
|
MessageFlags,
|
||||||
StringSelectMenuInteraction
|
StringSelectMenuInteraction,
|
||||||
} from 'discord.js';
|
} from "discord.js";
|
||||||
import { prisma } from '../../../core/database/prisma';
|
import { prisma } from "../../../core/database/prisma";
|
||||||
import { getOrCreateWallet, buyFromOffer } from '../../../game/economy/service';
|
import { getOrCreateWallet, buyFromOffer } from "../../../game/economy/service";
|
||||||
import type { DisplayComponentContainer } from '../../../core/types/displayComponents';
|
import type { DisplayComponentContainer } from "../../../core/types/displayComponents";
|
||||||
import type { ItemProps } from '../../../game/economy/types';
|
import type { ItemProps } from "../../../game/economy/types";
|
||||||
import { formatItemLabel, resolveItemIcon } from './_helpers';
|
import { formatItemLabel, resolveItemIcon } from "./_helpers";
|
||||||
|
|
||||||
const ITEMS_PER_PAGE = 5;
|
const ITEMS_PER_PAGE = 5;
|
||||||
|
|
||||||
function parseItemProps(json: unknown): ItemProps {
|
function parseItemProps(json: unknown): ItemProps {
|
||||||
if (!json || typeof json !== 'object') return {};
|
if (!json || typeof json !== "object") return {};
|
||||||
return json as ItemProps;
|
return json as ItemProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,36 +30,37 @@ function formatPrice(price: any): string {
|
|||||||
parts.push(`📦 ${item.itemKey} x${item.qty}`);
|
parts.push(`📦 ${item.itemKey} x${item.qty}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return parts.join(' + ') || '¿Gratis?';
|
return parts.join(" + ") || "¿Gratis?";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getItemIcon(props: ItemProps, category?: string): string {
|
function getItemIcon(props: ItemProps, category?: string): string {
|
||||||
if (props.tool) {
|
if (props.tool) {
|
||||||
const t = props.tool.type;
|
const t = props.tool.type;
|
||||||
if (t === 'pickaxe') return '⛏️';
|
if (t === "pickaxe") return "⛏️";
|
||||||
if (t === 'rod') return '🎣';
|
if (t === "rod") return "🎣";
|
||||||
if (t === 'sword') return '🗡️';
|
if (t === "sword") return "🗡️";
|
||||||
if (t === 'bow') return '🏹';
|
if (t === "bow") return "🏹";
|
||||||
if (t === 'halberd') return '⚔️';
|
if (t === "halberd") return "⚔️";
|
||||||
if (t === 'net') return '🕸️';
|
if (t === "net") return "🕸️";
|
||||||
return '🔧';
|
return "🔧";
|
||||||
}
|
}
|
||||||
if (props.damage && props.damage > 0) return '⚔️';
|
if (props.damage && props.damage > 0) return "⚔️";
|
||||||
if (props.defense && props.defense > 0) return '🛡️';
|
if (props.defense && props.defense > 0) return "🛡️";
|
||||||
if (props.food) return '🍖';
|
if (props.food) return "🍖";
|
||||||
if (props.chest) return '📦';
|
if (props.chest) return "📦";
|
||||||
if (category === 'consumables') return '🧪';
|
if (category === "consumables") return "🧪";
|
||||||
if (category === 'materials') return '🔨';
|
if (category === "materials") return "🔨";
|
||||||
return '📦';
|
return "📦";
|
||||||
}
|
}
|
||||||
|
|
||||||
export const command: CommandMessage = {
|
export const command: CommandMessage = {
|
||||||
name: 'tienda',
|
name: "tienda",
|
||||||
type: 'message',
|
type: "message",
|
||||||
aliases: ['shop', 'store'],
|
aliases: ["shop", "store"],
|
||||||
cooldown: 5,
|
cooldown: 5,
|
||||||
description: 'Abre la tienda y navega por las ofertas disponibles con un panel interactivo.',
|
description:
|
||||||
usage: 'tienda [categoria]',
|
"Abre la tienda y navega por las ofertas disponibles con un panel interactivo.",
|
||||||
|
usage: "tienda [categoria]",
|
||||||
run: async (message, args, _client: Amayo) => {
|
run: async (message, args, _client: Amayo) => {
|
||||||
const userId = message.author.id;
|
const userId = message.author.id;
|
||||||
const guildId = message.guild!.id;
|
const guildId = message.guild!.id;
|
||||||
@@ -77,89 +78,104 @@ export const command: CommandMessage = {
|
|||||||
{ startAt: null, endAt: null },
|
{ startAt: null, endAt: null },
|
||||||
{ startAt: { lte: now }, endAt: { gte: now } },
|
{ startAt: { lte: now }, endAt: { gte: now } },
|
||||||
{ startAt: { lte: now }, endAt: null },
|
{ startAt: { lte: now }, endAt: null },
|
||||||
{ startAt: null, endAt: { gte: now } }
|
{ startAt: null, endAt: { gte: now } },
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
include: { item: true },
|
include: { item: true },
|
||||||
orderBy: { createdAt: 'desc' }
|
orderBy: { createdAt: "desc" },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (offers.length === 0) {
|
if (offers.length === 0) {
|
||||||
await message.reply('🏪 La tienda está vacía. ¡Vuelve más tarde!');
|
await message.reply(
|
||||||
|
"<a:seven:1425666197466255481> La tienda está vacía. ¡Vuelve más tarde!"
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filtrar por categoría si se proporciona
|
// Filtrar por categoría si se proporciona
|
||||||
const categoryFilter = args[0]?.trim().toLowerCase();
|
const categoryFilter = args[0]?.trim().toLowerCase();
|
||||||
const filteredOffers = categoryFilter
|
const filteredOffers = categoryFilter
|
||||||
? offers.filter(o => o.item.category?.toLowerCase().includes(categoryFilter))
|
? offers.filter((o) =>
|
||||||
|
o.item.category?.toLowerCase().includes(categoryFilter)
|
||||||
|
)
|
||||||
: offers;
|
: offers;
|
||||||
|
|
||||||
if (filteredOffers.length === 0) {
|
if (filteredOffers.length === 0) {
|
||||||
await message.reply(`🏪 No hay ofertas en la categoría "${categoryFilter}".`);
|
await message.reply(
|
||||||
|
`<a:seven:1425666197466255481> No hay ofertas en la categoría "${categoryFilter}".`
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Estado inicial
|
// Estado inicial
|
||||||
const sessionState = {
|
const sessionState = {
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
selectedOfferId: null as string | null
|
selectedOfferId: null as string | null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const shopMessage = await message.reply({
|
const shopMessage = await message.reply({
|
||||||
flags: MessageFlags.SuppressEmbeds | 32768,
|
flags: MessageFlags.SuppressEmbeds | 32768,
|
||||||
components: await buildShopPanel(filteredOffers, sessionState.currentPage, wallet.coins, sessionState.selectedOfferId)
|
components: await buildShopPanel(
|
||||||
|
filteredOffers,
|
||||||
|
sessionState.currentPage,
|
||||||
|
wallet.coins,
|
||||||
|
sessionState.selectedOfferId
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Collector para interacciones
|
// Collector para interacciones
|
||||||
const collector = shopMessage.createMessageComponentCollector({
|
const collector = shopMessage.createMessageComponentCollector({
|
||||||
time: 300000, // 5 minutos
|
time: 300000, // 5 minutos
|
||||||
filter: (i: MessageComponentInteraction) => i.user.id === message.author.id
|
filter: (i: MessageComponentInteraction) =>
|
||||||
|
i.user.id === message.author.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
collector.on('collect', async (interaction: MessageComponentInteraction) => {
|
collector.on(
|
||||||
try {
|
"collect",
|
||||||
if (interaction.isButton()) {
|
async (interaction: MessageComponentInteraction) => {
|
||||||
await handleButtonInteraction(
|
try {
|
||||||
interaction as ButtonInteraction,
|
if (interaction.isButton()) {
|
||||||
filteredOffers,
|
await handleButtonInteraction(
|
||||||
sessionState,
|
interaction as ButtonInteraction,
|
||||||
userId,
|
filteredOffers,
|
||||||
guildId,
|
sessionState,
|
||||||
shopMessage,
|
userId,
|
||||||
collector
|
guildId,
|
||||||
);
|
shopMessage,
|
||||||
} else if (interaction.isStringSelectMenu()) {
|
collector
|
||||||
await handleSelectInteraction(
|
);
|
||||||
interaction as StringSelectMenuInteraction,
|
} else if (interaction.isStringSelectMenu()) {
|
||||||
filteredOffers,
|
await handleSelectInteraction(
|
||||||
sessionState.currentPage,
|
interaction as StringSelectMenuInteraction,
|
||||||
userId,
|
filteredOffers,
|
||||||
guildId,
|
sessionState.currentPage,
|
||||||
shopMessage
|
userId,
|
||||||
);
|
guildId,
|
||||||
}
|
shopMessage
|
||||||
} catch (error: any) {
|
);
|
||||||
console.error('Error handling shop interaction:', error);
|
}
|
||||||
if (!interaction.replied && !interaction.deferred) {
|
} catch (error: any) {
|
||||||
await interaction.reply({
|
console.error("Error handling shop interaction:", error);
|
||||||
content: `❌ Error: ${error?.message ?? error}`,
|
if (!interaction.replied && !interaction.deferred) {
|
||||||
flags: MessageFlags.Ephemeral
|
await interaction.reply({
|
||||||
});
|
content: `❌ Error: ${error?.message ?? error}`,
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
collector.on('end', async (_, reason) => {
|
collector.on("end", async (_, reason) => {
|
||||||
if (reason === 'time') {
|
if (reason === "time") {
|
||||||
try {
|
try {
|
||||||
await shopMessage.edit({
|
await shopMessage.edit({
|
||||||
components: await buildExpiredPanel()
|
components: await buildExpiredPanel(),
|
||||||
});
|
});
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
async function buildShopPanel(
|
async function buildShopPanel(
|
||||||
@@ -175,7 +191,7 @@ async function buildShopPanel(
|
|||||||
|
|
||||||
// Encontrar la oferta seleccionada
|
// Encontrar la oferta seleccionada
|
||||||
const selectedOffer = selectedOfferId
|
const selectedOffer = selectedOfferId
|
||||||
? offers.find(o => o.id === selectedOfferId)
|
? offers.find((o) => o.id === selectedOfferId)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
// Container principal
|
// Container principal
|
||||||
@@ -185,85 +201,109 @@ async function buildShopPanel(
|
|||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
type: 10,
|
type: 10,
|
||||||
content: `🏪 **TIENDA** | Página ${safePage}/${totalPages}\n💰 Tus Monedas: **${userCoins}**`
|
content: `# <a:seven:1425666197466255481> Tienda - Ofertas Disponibles`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `-# <:coin:1425667511013081169> Monedas: **${userCoins}**`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 14,
|
type: 14,
|
||||||
divider: true,
|
divider: true,
|
||||||
spacing: 2
|
spacing: 2,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Si hay una oferta seleccionada, mostrar detalles
|
// Si hay una oferta seleccionada, mostrar detalles
|
||||||
if (selectedOffer) {
|
if (selectedOffer) {
|
||||||
const item = selectedOffer.item;
|
const item = selectedOffer.item;
|
||||||
const props = parseItemProps(item.props);
|
const props = parseItemProps(item.props);
|
||||||
const label = formatItemLabel(item, { fallbackIcon: getItemIcon(props, item.category), bold: true });
|
const label = formatItemLabel(item, {
|
||||||
|
fallbackIcon: getItemIcon(props, item.category),
|
||||||
|
bold: true,
|
||||||
|
});
|
||||||
const price = formatPrice(selectedOffer.price);
|
const price = formatPrice(selectedOffer.price);
|
||||||
|
|
||||||
// Stock info
|
// Stock info
|
||||||
let stockInfo = '';
|
let stockInfo = "";
|
||||||
if (selectedOffer.stock != null) {
|
if (selectedOffer.stock != null) {
|
||||||
stockInfo = `\n📊 Stock: ${selectedOffer.stock}`;
|
stockInfo = `\n<:clipboard:1425669350316048435> Stock: ${selectedOffer.stock}`;
|
||||||
}
|
}
|
||||||
if (selectedOffer.perUserLimit != null) {
|
if (selectedOffer.perUserLimit != null) {
|
||||||
const purchased = await prisma.shopPurchase.aggregate({
|
const purchased = await prisma.shopPurchase.aggregate({
|
||||||
where: { offerId: selectedOffer.id },
|
where: { offerId: selectedOffer.id },
|
||||||
_sum: { qty: true }
|
_sum: { qty: true },
|
||||||
});
|
});
|
||||||
const userPurchased = purchased._sum.qty ?? 0;
|
const userPurchased = purchased._sum.qty ?? 0;
|
||||||
stockInfo += `\n👤 Límite por usuario: ${userPurchased}/${selectedOffer.perUserLimit}`;
|
stockInfo += `\n<:Sup_urg:1420535068056748042> Límite por usuario: ${userPurchased}/${selectedOffer.perUserLimit}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stats del item
|
// Stats del item
|
||||||
let statsInfo = '';
|
let statsInfo = "";
|
||||||
if (props.damage) statsInfo += `\n⚔️ Daño: +${props.damage}`;
|
if (props.damage)
|
||||||
if (props.defense) statsInfo += `\n🛡️ Defensa: +${props.defense}`;
|
statsInfo += `\n<:damage:1425670476449189998> Daño: +${props.damage}`;
|
||||||
if (props.maxHpBonus) statsInfo += `\n❤️ HP Bonus: +${props.maxHpBonus}`;
|
if (props.defense)
|
||||||
if (props.tool) statsInfo += `\n🔧 Herramienta: ${props.tool.type} T${props.tool.tier ?? 1}`;
|
statsInfo += `\n<:defens:1425670433910427862> Defensa: +${props.defense}`;
|
||||||
if (props.food && props.food.healHp) statsInfo += `\n🍖 Cura: ${props.food.healHp} HP`;
|
if (props.maxHpBonus)
|
||||||
|
statsInfo += `\n<:healbonus:1425671499792121877> HP Bonus: +${props.maxHpBonus}`;
|
||||||
|
if (props.tool)
|
||||||
|
statsInfo += `\n<:table:1425673712312782879> Herramienta: ${
|
||||||
|
props.tool.type
|
||||||
|
} T${props.tool.tier ?? 1}`;
|
||||||
|
if (props.food && props.food.healHp)
|
||||||
|
statsInfo += `\n<:cure:1425671519639572510> Cura: ${props.food.healHp} HP`;
|
||||||
|
|
||||||
container.components.push({
|
container.components.push({
|
||||||
type: 10,
|
type: 10,
|
||||||
content: `${label}\n\n${item.description || 'Sin descripción'}${statsInfo}\n\n💰 Precio: ${price}${stockInfo}`
|
content: `${label}\n\n${
|
||||||
|
item.description || "Sin descripción"
|
||||||
|
}${statsInfo}\n\n<:price:1425673879094820906> Precio: ${price}${stockInfo}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
container.components.push({
|
container.components.push({
|
||||||
type: 14,
|
type: 14,
|
||||||
divider: true,
|
divider: true,
|
||||||
spacing: 1
|
spacing: 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lista de ofertas en la página
|
// Lista de ofertas en la página
|
||||||
container.components.push({
|
container.components.push({
|
||||||
type: 10,
|
type: 10,
|
||||||
content: selectedOffer ? '📋 **Otras Ofertas:**' : '📋 **Ofertas Disponibles:**'
|
content: selectedOffer
|
||||||
|
? "<:clipboard:1425669350316048435> **Otras Ofertas:**"
|
||||||
|
: "<:clipboard:1425669350316048435> **Ofertas Disponibles:**",
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const offer of pageOffers) {
|
for (const offer of pageOffers) {
|
||||||
const item = offer.item;
|
const item = offer.item;
|
||||||
const props = parseItemProps(item.props);
|
const props = parseItemProps(item.props);
|
||||||
const label = formatItemLabel(item, { fallbackIcon: getItemIcon(props, item.category), bold: true });
|
const label = formatItemLabel(item, {
|
||||||
|
fallbackIcon: getItemIcon(props, item.category),
|
||||||
|
bold: true,
|
||||||
|
});
|
||||||
const price = formatPrice(offer.price);
|
const price = formatPrice(offer.price);
|
||||||
const isSelected = selectedOfferId === offer.id;
|
const isSelected = selectedOfferId === offer.id;
|
||||||
|
|
||||||
const stockText = offer.stock != null ? ` (${offer.stock} disponibles)` : '';
|
const stockText =
|
||||||
const selectedMark = isSelected ? ' ✓' : '';
|
offer.stock != null ? ` (${offer.stock} disponibles)` : "";
|
||||||
|
const selectedMark = isSelected ? " ✓" : "";
|
||||||
|
|
||||||
container.components.push({
|
container.components.push({
|
||||||
type: 9,
|
type: 9,
|
||||||
components: [{
|
components: [
|
||||||
type: 10,
|
{
|
||||||
content: `${label}${selectedMark}\n💰 ${price}${stockText}`
|
type: 10,
|
||||||
}],
|
content: `${label}${selectedMark}\n<:coin:1425667511013081169> ${price}${stockText}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
accessory: {
|
accessory: {
|
||||||
type: 2,
|
type: 2,
|
||||||
style: isSelected ? ButtonStyle.Success : ButtonStyle.Primary,
|
style: isSelected ? ButtonStyle.Success : ButtonStyle.Primary,
|
||||||
label: isSelected ? 'Seleccionado' : 'Ver',
|
label: isSelected ? "Seleccionado" : "Ver",
|
||||||
custom_id: `shop_view_${offer.id}`
|
custom_id: `shop_view_${offer.id}`,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,25 +314,25 @@ async function buildShopPanel(
|
|||||||
{
|
{
|
||||||
type: ComponentType.Button,
|
type: ComponentType.Button,
|
||||||
style: ButtonStyle.Secondary,
|
style: ButtonStyle.Secondary,
|
||||||
label: '◀️ Anterior',
|
label: "◀️ Anterior",
|
||||||
custom_id: 'shop_prev_page',
|
custom_id: "shop_prev_page",
|
||||||
disabled: safePage <= 1
|
disabled: safePage <= 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: ComponentType.Button,
|
type: ComponentType.Button,
|
||||||
style: ButtonStyle.Secondary,
|
style: ButtonStyle.Secondary,
|
||||||
label: `Página ${safePage}/${totalPages}`,
|
label: `Página ${safePage}/${totalPages}`,
|
||||||
custom_id: 'shop_current_page',
|
custom_id: "shop_current_page",
|
||||||
disabled: true
|
disabled: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: ComponentType.Button,
|
type: ComponentType.Button,
|
||||||
style: ButtonStyle.Secondary,
|
style: ButtonStyle.Secondary,
|
||||||
label: 'Siguiente ▶️',
|
label: "Siguiente ▶️",
|
||||||
custom_id: 'shop_next_page',
|
custom_id: "shop_next_page",
|
||||||
disabled: safePage >= totalPages
|
disabled: safePage >= totalPages,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const actionRow2 = {
|
const actionRow2 = {
|
||||||
@@ -301,30 +341,30 @@ async function buildShopPanel(
|
|||||||
{
|
{
|
||||||
type: ComponentType.Button,
|
type: ComponentType.Button,
|
||||||
style: ButtonStyle.Success,
|
style: ButtonStyle.Success,
|
||||||
label: '🛒 Comprar (x1)',
|
label: "🛒 Comprar (x1)",
|
||||||
custom_id: 'shop_buy_1',
|
custom_id: "shop_buy_1",
|
||||||
disabled: !selectedOfferId
|
disabled: !selectedOfferId,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: ComponentType.Button,
|
type: ComponentType.Button,
|
||||||
style: ButtonStyle.Success,
|
style: ButtonStyle.Success,
|
||||||
label: '🛒 Comprar (x5)',
|
label: "🛒 Comprar (x5)",
|
||||||
custom_id: 'shop_buy_5',
|
custom_id: "shop_buy_5",
|
||||||
disabled: !selectedOfferId
|
disabled: !selectedOfferId,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: ComponentType.Button,
|
type: ComponentType.Button,
|
||||||
style: ButtonStyle.Primary,
|
style: ButtonStyle.Primary,
|
||||||
label: '🔄 Actualizar',
|
label: "🔄 Actualizar",
|
||||||
custom_id: 'shop_refresh'
|
custom_id: "shop_refresh",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: ComponentType.Button,
|
type: ComponentType.Button,
|
||||||
style: ButtonStyle.Danger,
|
style: ButtonStyle.Danger,
|
||||||
label: '❌ Cerrar',
|
label: "❌ Cerrar",
|
||||||
custom_id: 'shop_close'
|
custom_id: "shop_close",
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
return [container, actionRow1, actionRow2];
|
return [container, actionRow1, actionRow2];
|
||||||
@@ -342,78 +382,95 @@ async function handleButtonInteraction(
|
|||||||
const customId = interaction.customId;
|
const customId = interaction.customId;
|
||||||
|
|
||||||
// Ver detalles de un item
|
// Ver detalles de un item
|
||||||
if (customId.startsWith('shop_view_')) {
|
if (customId.startsWith("shop_view_")) {
|
||||||
const offerId = customId.replace('shop_view_', '');
|
const offerId = customId.replace("shop_view_", "");
|
||||||
const wallet = await getOrCreateWallet(userId, guildId);
|
const wallet = await getOrCreateWallet(userId, guildId);
|
||||||
sessionState.selectedOfferId = offerId;
|
sessionState.selectedOfferId = offerId;
|
||||||
|
|
||||||
await interaction.update({
|
await interaction.update({
|
||||||
components: await buildShopPanel(offers, sessionState.currentPage, wallet.coins, sessionState.selectedOfferId)
|
components: await buildShopPanel(
|
||||||
|
offers,
|
||||||
|
sessionState.currentPage,
|
||||||
|
wallet.coins,
|
||||||
|
sessionState.selectedOfferId
|
||||||
|
),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comprar
|
// Comprar
|
||||||
if (customId === 'shop_buy_1' || customId === 'shop_buy_5') {
|
if (customId === "shop_buy_1" || customId === "shop_buy_5") {
|
||||||
const selectedOfferId = sessionState.selectedOfferId;
|
const selectedOfferId = sessionState.selectedOfferId;
|
||||||
if (!selectedOfferId) {
|
if (!selectedOfferId) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: '❌ Primero selecciona un item.',
|
content: "❌ Primero selecciona un item.",
|
||||||
flags: MessageFlags.Ephemeral
|
flags: MessageFlags.Ephemeral,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const qty = customId === 'shop_buy_1' ? 1 : 5;
|
const qty = customId === "shop_buy_1" ? 1 : 5;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await interaction.deferUpdate();
|
await interaction.deferUpdate();
|
||||||
const result = await buyFromOffer(userId, guildId, selectedOfferId, qty);
|
const result = await buyFromOffer(userId, guildId, selectedOfferId, qty);
|
||||||
const wallet = await getOrCreateWallet(userId, guildId);
|
const wallet = await getOrCreateWallet(userId, guildId);
|
||||||
|
|
||||||
const purchaseLabel = formatItemLabel(result.item, { fallbackIcon: resolveItemIcon(result.item.icon) });
|
const purchaseLabel = formatItemLabel(result.item, {
|
||||||
|
fallbackIcon: resolveItemIcon(result.item.icon),
|
||||||
|
});
|
||||||
await interaction.followUp({
|
await interaction.followUp({
|
||||||
content: `✅ **Compra exitosa!**\n🛒 ${purchaseLabel} x${result.qty}\n💰 Te quedan: ${wallet.coins} monedas`,
|
content: `✅ **Compra exitosa!**\n🛒 ${purchaseLabel} x${result.qty}\n<:coin:1425667511013081169> Te quedan: ${wallet.coins} monedas`,
|
||||||
flags: MessageFlags.Ephemeral
|
flags: MessageFlags.Ephemeral,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Actualizar tienda
|
// Actualizar tienda
|
||||||
await shopMessage.edit({
|
await shopMessage.edit({
|
||||||
components: await buildShopPanel(offers, sessionState.currentPage, wallet.coins, sessionState.selectedOfferId)
|
components: await buildShopPanel(
|
||||||
|
offers,
|
||||||
|
sessionState.currentPage,
|
||||||
|
wallet.coins,
|
||||||
|
sessionState.selectedOfferId
|
||||||
|
),
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
await interaction.followUp({
|
await interaction.followUp({
|
||||||
content: `❌ No se pudo comprar: ${error?.message ?? error}`,
|
content: `❌ No se pudo comprar: ${error?.message ?? error}`,
|
||||||
flags: MessageFlags.Ephemeral
|
flags: MessageFlags.Ephemeral,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualizar
|
// Actualizar
|
||||||
if (customId === 'shop_refresh') {
|
if (customId === "shop_refresh") {
|
||||||
const wallet = await getOrCreateWallet(userId, guildId);
|
const wallet = await getOrCreateWallet(userId, guildId);
|
||||||
await interaction.update({
|
await interaction.update({
|
||||||
components: await buildShopPanel(offers, sessionState.currentPage, wallet.coins, sessionState.selectedOfferId)
|
components: await buildShopPanel(
|
||||||
|
offers,
|
||||||
|
sessionState.currentPage,
|
||||||
|
wallet.coins,
|
||||||
|
sessionState.selectedOfferId
|
||||||
|
),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cerrar
|
// Cerrar
|
||||||
if (customId === 'shop_close') {
|
if (customId === "shop_close") {
|
||||||
await interaction.update({
|
await interaction.update({
|
||||||
components: await buildClosedPanel()
|
components: await buildClosedPanel(),
|
||||||
});
|
});
|
||||||
collector.stop();
|
collector.stop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navegación de páginas (ya manejado en el collect)
|
// Navegación de páginas (ya manejado en el collect)
|
||||||
if (customId === 'shop_prev_page' || customId === 'shop_next_page') {
|
if (customId === "shop_prev_page" || customId === "shop_next_page") {
|
||||||
const wallet = await getOrCreateWallet(userId, guildId);
|
const wallet = await getOrCreateWallet(userId, guildId);
|
||||||
let newPage = sessionState.currentPage;
|
let newPage = sessionState.currentPage;
|
||||||
|
|
||||||
if (customId === 'shop_prev_page') {
|
if (customId === "shop_prev_page") {
|
||||||
newPage = Math.max(1, sessionState.currentPage - 1);
|
newPage = Math.max(1, sessionState.currentPage - 1);
|
||||||
} else {
|
} else {
|
||||||
const totalPages = Math.ceil(offers.length / ITEMS_PER_PAGE);
|
const totalPages = Math.ceil(offers.length / ITEMS_PER_PAGE);
|
||||||
@@ -423,7 +480,12 @@ async function handleButtonInteraction(
|
|||||||
sessionState.currentPage = newPage;
|
sessionState.currentPage = newPage;
|
||||||
|
|
||||||
await interaction.update({
|
await interaction.update({
|
||||||
components: await buildShopPanel(offers, sessionState.currentPage, wallet.coins, sessionState.selectedOfferId)
|
components: await buildShopPanel(
|
||||||
|
offers,
|
||||||
|
sessionState.currentPage,
|
||||||
|
wallet.coins,
|
||||||
|
sessionState.selectedOfferId
|
||||||
|
),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -439,8 +501,8 @@ async function handleSelectInteraction(
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Si implementas un select menu, manejar aquí
|
// Si implementas un select menu, manejar aquí
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: 'Select menu no implementado aún',
|
content: "Select menu no implementado aún",
|
||||||
flags: MessageFlags.Ephemeral
|
flags: MessageFlags.Ephemeral,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -451,18 +513,19 @@ async function buildExpiredPanel(): Promise<any[]> {
|
|||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
type: 10,
|
type: 10,
|
||||||
content: '⏰ **Tienda Expirada**'
|
content: "⏰ **Tienda Expirada**",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 14,
|
type: 14,
|
||||||
divider: true,
|
divider: true,
|
||||||
spacing: 1
|
spacing: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 10,
|
type: 10,
|
||||||
content: 'La sesión de tienda ha expirado.\nUsa `!tienda` nuevamente para ver las ofertas.'
|
content:
|
||||||
}
|
"La sesión de tienda ha expirado.\nUsa `!tienda` nuevamente para ver las ofertas.",
|
||||||
]
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
return [container];
|
return [container];
|
||||||
@@ -475,18 +538,18 @@ async function buildClosedPanel(): Promise<any[]> {
|
|||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
type: 10,
|
type: 10,
|
||||||
content: '✅ **Tienda Cerrada**'
|
content: "✅ **Tienda Cerrada**",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 14,
|
type: 14,
|
||||||
divider: true,
|
divider: true,
|
||||||
spacing: 1
|
spacing: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 10,
|
type: 10,
|
||||||
content: '¡Gracias por visitar la tienda!\nVuelve pronto. 🛒'
|
content: "¡Gracias por visitar la tienda!\nVuelve pronto. 🛒",
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
return [container];
|
return [container];
|
||||||
|
|||||||
Reference in New Issue
Block a user