From 7e9aa06556b0221a9d5bb5aa2148c466b5fcb8e3 Mon Sep 17 00:00:00 2001 From: syntaxbullet Date: Sat, 13 Dec 2025 16:02:07 +0100 Subject: [PATCH] feat: Introduce success and info embeds, add related user tracking to economy transactions, and refine trade interaction feedback and thread cleanup. --- src/lib/embeds.ts | 28 +++++++++ src/modules/economy/economy.service.ts | 3 +- src/modules/inventory/inventory.service.ts | 2 +- src/modules/quest/quest.service.ts | 2 +- src/modules/trade/trade.interaction.ts | 66 ++++++++++++++++++---- src/modules/trade/trade.service.ts | 2 + 6 files changed, 88 insertions(+), 15 deletions(-) diff --git a/src/lib/embeds.ts b/src/lib/embeds.ts index 36f1ed7..15cd05c 100644 --- a/src/lib/embeds.ts +++ b/src/lib/embeds.ts @@ -27,3 +27,31 @@ export function createWarningEmbed(message: string, title: string = "Warning"): .setColor(Colors.Yellow) .setTimestamp(); } + +/** + * Creates a standardized success embed. + * @param message The success message to display. + * @param title Optional title for the embed. Defaults to "Success". + * @returns An EmbedBuilder instance configured as a success. + */ +export function createSuccessEmbed(message: string, title: string = "Success"): EmbedBuilder { + return new EmbedBuilder() + .setTitle(`✅ ${title}`) + .setDescription(message) + .setColor(Colors.Green) + .setTimestamp(); +} + +/** + * Creates a standardized info embed. + * @param message The info message to display. + * @param title Optional title for the embed. Defaults to "Info". + * @returns An EmbedBuilder instance configured as info. + */ +export function createInfoEmbed(message: string, title: string = "Info"): EmbedBuilder { + return new EmbedBuilder() + .setTitle(`â„šī¸ ${title}`) + .setDescription(message) + .setColor(Colors.Blue) + .setTimestamp(); +} diff --git a/src/modules/economy/economy.service.ts b/src/modules/economy/economy.service.ts index 548bd00..d90cc1e 100644 --- a/src/modules/economy/economy.service.ts +++ b/src/modules/economy/economy.service.ts @@ -159,7 +159,7 @@ export const economyService = { } }, - modifyUserBalance: async (id: string, amount: bigint, type: string, description: string, tx?: any) => { + modifyUserBalance: async (id: string, amount: bigint, type: string, description: string, relatedUserId?: string | null, tx?: any) => { const execute = async (txFn: any) => { if (amount < 0n) { // Check sufficient funds if removing @@ -180,6 +180,7 @@ export const economyService = { await txFn.insert(transactions).values({ userId: BigInt(id), + relatedUserId: relatedUserId ? BigInt(relatedUserId) : null, amount: amount, type: type, description: description, diff --git a/src/modules/inventory/inventory.service.ts b/src/modules/inventory/inventory.service.ts index c8dd866..8e2fe28 100644 --- a/src/modules/inventory/inventory.service.ts +++ b/src/modules/inventory/inventory.service.ts @@ -118,7 +118,7 @@ export const inventoryService = { const totalPrice = item.price * quantity; // Deduct Balance using economy service (passing tx ensures atomicity) - await economyService.modifyUserBalance(userId, -totalPrice, 'PURCHASE', `Bought ${quantity}x ${item.name}`, txFn); + await economyService.modifyUserBalance(userId, -totalPrice, 'PURCHASE', `Bought ${quantity}x ${item.name}`, null, txFn); await inventoryService.addItem(userId, itemId, quantity, txFn); diff --git a/src/modules/quest/quest.service.ts b/src/modules/quest/quest.service.ts index 9d4cbd3..9128533 100644 --- a/src/modules/quest/quest.service.ts +++ b/src/modules/quest/quest.service.ts @@ -62,7 +62,7 @@ export const questService = { if (rewards?.balance) { const bal = BigInt(rewards.balance); - await economyService.modifyUserBalance(userId, bal, 'QUEST_REWARD', `Reward for quest ${questId}`, txFn); + await economyService.modifyUserBalance(userId, bal, 'QUEST_REWARD', `Reward for quest ${questId}`, null, txFn); results.balance = bal; } diff --git a/src/modules/trade/trade.interaction.ts b/src/modules/trade/trade.interaction.ts index 63e3031..9529b29 100644 --- a/src/modules/trade/trade.interaction.ts +++ b/src/modules/trade/trade.interaction.ts @@ -16,7 +16,7 @@ import { } from "discord.js"; import { TradeService } from "./trade.service"; import { inventoryService } from "@/modules/inventory/inventory.service"; -import { createErrorEmbed, createWarningEmbed } from "@lib/embeds"; +import { createErrorEmbed, createWarningEmbed, createSuccessEmbed, createInfoEmbed } from "@lib/embeds"; const EMBED_COLOR = 0xFFD700; // Gold @@ -61,18 +61,21 @@ export async function handleTradeInteraction(interaction: Interaction) { } async function handleCancel(interaction: ButtonInteraction | StringSelectMenuInteraction | ModalSubmitInteraction, threadId: string) { + const session = TradeService.getSession(threadId); + const user = interaction.user; + TradeService.endSession(threadId); - await interaction.reply({ content: "🛑 Trade cancelled. Deleting thread in 5 seconds..." }); - setTimeout(async () => { - try { - await interaction.channel?.delete(); - } catch (e) { - console.error("Failed to delete thread", e); - } - }, 5000); + + await interaction.deferUpdate(); + + if (interaction.channel?.isThread()) { + const embed = createInfoEmbed(`Trade cancelled by ${user.username}.`, "Trade Cancelled"); + await scheduleThreadCleanup(interaction.channel, "This thread will be deleted in 5 seconds.", 5000, embed); + } } async function handleLock(interaction: ButtonInteraction | StringSelectMenuInteraction | ModalSubmitInteraction, threadId: string) { + await interaction.deferUpdate(); const isLocked = TradeService.toggleLock(threadId, interaction.user.id); await updateTradeDashboard(interaction, threadId); @@ -214,12 +217,29 @@ export async function updateTradeDashboard(interaction: Interaction, threadId: s .setTimestamp(); await updateDashboardMessage(interaction, { embeds: [embed], components: [] }); + + // Notify and Schedule Cleanup + if (interaction.channel?.isThread()) { + const successEmbed = createSuccessEmbed("Trade executed successfully. Items and funds have been transferred.", "Trade Complete"); + await scheduleThreadCleanup( + interaction.channel, + `🎉 Trade successful! <@${session.userA.id}> <@${session.userB.id}>\nThis thread will be deleted in 10 seconds.`, + 10000, + successEmbed + ); + } return; } catch (e: any) { - const embed = createErrorEmbed(e.message, "Trade Failed"); + console.error("Trade Execution Error:", e); + const errorEmbed = createErrorEmbed(e.message, "Trade Failed"); - if (interaction.channel && (interaction.channel.isThread() || interaction.channel instanceof TextChannel)) { - await interaction.channel.send({ embeds: [embed] }); + if (interaction.channel?.isThread()) { + await scheduleThreadCleanup( + interaction.channel, + "❌ Trade failed due to an error. This thread will be deleted in 10 seconds.", + 10000, + errorEmbed + ); } return; } @@ -291,3 +311,25 @@ function formatOffer(participant: any) { if (text === "") text = "*Empty Offer*"; return text; } + +async function scheduleThreadCleanup(channel: ThreadChannel | TextChannel, message: string, delayMs: number = 10000, embed?: EmbedBuilder) { + try { + const payload: any = { content: message }; + if (embed) payload.embeds = [embed]; + + await channel.send(payload); + + setTimeout(async () => { + try { + if (channel.isThread()) { + console.log(`Deleting thread: ${channel.id}`); + await channel.delete("Trade Session Ended"); + } + } catch (e) { + console.error("Failed to delete thread", e); + } + }, delayMs); + } catch (e) { + console.error("Failed to send cleanup notification", e); + } +} diff --git a/src/modules/trade/trade.service.ts b/src/modules/trade/trade.service.ts index 1768591..93056e1 100644 --- a/src/modules/trade/trade.service.ts +++ b/src/modules/trade/trade.service.ts @@ -144,6 +144,7 @@ export class TradeService { -from.offer.money, 'TRADE_OUT', `Trade with ${to.username} (Thread: ${threadId})`, + to.id, tx ); await economyService.modifyUserBalance( @@ -151,6 +152,7 @@ export class TradeService { from.offer.money, 'TRADE_IN', `Trade with ${from.username} (Thread: ${threadId})`, + from.id, tx ); }