feat: Introduce success and info embeds, add related user tracking to economy transactions, and refine trade interaction feedback and thread cleanup.
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
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);
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user