AeThex-Bot-Master/aethex-bot/commands/trade.js
sirpiglr c2a34f398e Add server mode configuration and dynamic status updates
Introduces a new server mode configuration system (Federation/Standalone) with associated command changes, dynamic status rotation for the bot, and adds new commands and features.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: b08e6ba5-7498-4b9f-b1c9-7dc11b362ddd
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/R9PkDi8
Replit-Helium-Checkpoint-Created: true
2025-12-09 23:26:33 +00:00

218 lines
7.1 KiB
JavaScript

const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js');
const { getServerMode, getEmbedColor, EMBED_COLORS } = require('../utils/modeHelper');
const activeTrades = new Map();
module.exports = {
data: new SlashCommandBuilder()
.setName('trade')
.setDescription('Trade items with another user')
.addUserOption(option =>
option.setName('user')
.setDescription('User to trade with')
.setRequired(true)
)
.addStringOption(option =>
option.setName('offer')
.setDescription('What you\'re offering (item name)')
.setRequired(true)
)
.addStringOption(option =>
option.setName('request')
.setDescription('What you want in return (item name)')
.setRequired(true)
),
async execute(interaction, supabase, client) {
const partner = interaction.options.getUser('user');
const offer = interaction.options.getString('offer');
const request = interaction.options.getString('request');
const initiator = interaction.user;
const mode = await getServerMode(supabase, interaction.guildId);
const guildId = interaction.guildId;
if (partner.id === initiator.id) {
return interaction.reply({ content: "You can't trade with yourself!", ephemeral: true });
}
if (partner.bot) {
return interaction.reply({ content: "You can't trade with bots!", ephemeral: true });
}
if (!supabase) {
return interaction.reply({ content: 'Trade system unavailable.', ephemeral: true });
}
const tradeKey = `${guildId}-${initiator.id}`;
if (activeTrades.has(tradeKey)) {
return interaction.reply({ content: 'You already have an active trade!', ephemeral: true });
}
const { data: initiatorItem } = await supabase
.from('user_inventory')
.select('*, shop_items(*)')
.eq('guild_id', guildId)
.eq('user_id', initiator.id)
.ilike('shop_items.name', `%${offer}%`)
.gt('quantity', 0)
.maybeSingle();
if (!initiatorItem) {
return interaction.reply({
content: `You don't have "${offer}" in your inventory!`,
ephemeral: true
});
}
const { data: partnerItem } = await supabase
.from('user_inventory')
.select('*, shop_items(*)')
.eq('guild_id', guildId)
.eq('user_id', partner.id)
.ilike('shop_items.name', `%${request}%`)
.gt('quantity', 0)
.maybeSingle();
if (!partnerItem) {
return interaction.reply({
content: `${partner.username} doesn't have "${request}" in their inventory!`,
ephemeral: true
});
}
activeTrades.set(tradeKey, {
partner: partner.id,
offer: initiatorItem,
request: partnerItem
});
const embed = new EmbedBuilder()
.setColor(getEmbedColor(mode))
.setTitle('🔄 Trade Request')
.setDescription(`${initiator} wants to trade with ${partner}!`)
.addFields(
{
name: `${initiator.username} Offers`,
value: `${initiatorItem.shop_items?.emoji || '📦'} ${initiatorItem.shop_items?.name}`,
inline: true
},
{
name: `${partner.username} Offers`,
value: `${partnerItem.shop_items?.emoji || '📦'} ${partnerItem.shop_items?.name}`,
inline: true
}
)
.setFooter({ text: 'Trade expires in 60 seconds' })
.setTimestamp();
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId(`trade_accept_${initiator.id}`)
.setLabel('Accept')
.setStyle(ButtonStyle.Success),
new ButtonBuilder()
.setCustomId(`trade_decline_${initiator.id}`)
.setLabel('Decline')
.setStyle(ButtonStyle.Danger)
);
const message = await interaction.reply({
content: `${partner}`,
embeds: [embed],
components: [row],
fetchReply: true
});
const collector = message.createMessageComponentCollector({
filter: i => i.user.id === partner.id && i.customId.includes(initiator.id),
time: 60000,
max: 1
});
collector.on('collect', async (i) => {
const tradeData = activeTrades.get(tradeKey);
activeTrades.delete(tradeKey);
if (!tradeData) return;
if (i.customId.startsWith('trade_decline')) {
const declineEmbed = new EmbedBuilder()
.setColor(EMBED_COLORS.error)
.setTitle('🔄 Trade Declined')
.setDescription(`${partner} declined the trade.`)
.setTimestamp();
await i.update({ embeds: [declineEmbed], components: [] });
return;
}
try {
await supabase.rpc('execute_trade', {
p_guild_id: guildId,
p_user1_id: initiator.id,
p_user2_id: partner.id,
p_item1_id: tradeData.offer.item_id,
p_item2_id: tradeData.request.item_id
});
const successEmbed = new EmbedBuilder()
.setColor(EMBED_COLORS.success)
.setTitle('🔄 Trade Complete!')
.setDescription(`${initiator} and ${partner} successfully traded items!`)
.addFields(
{ name: `${initiator.username} received`, value: `${partnerItem.shop_items?.emoji || '📦'} ${partnerItem.shop_items?.name}`, inline: true },
{ name: `${partner.username} received`, value: `${initiatorItem.shop_items?.emoji || '📦'} ${initiatorItem.shop_items?.name}`, inline: true }
)
.setTimestamp();
await i.update({ embeds: [successEmbed], components: [] });
} catch (e) {
await supabase
.from('user_inventory')
.update({ quantity: tradeData.offer.quantity - 1 })
.eq('id', tradeData.offer.id);
await supabase
.from('user_inventory')
.update({ quantity: tradeData.request.quantity - 1 })
.eq('id', tradeData.request.id);
await supabase.from('user_inventory').upsert({
guild_id: guildId,
user_id: partner.id,
item_id: tradeData.offer.item_id,
quantity: 1
}, { onConflict: 'guild_id,user_id,item_id' });
await supabase.from('user_inventory').upsert({
guild_id: guildId,
user_id: initiator.id,
item_id: tradeData.request.item_id,
quantity: 1
}, { onConflict: 'guild_id,user_id,item_id' });
const successEmbed = new EmbedBuilder()
.setColor(EMBED_COLORS.success)
.setTitle('🔄 Trade Complete!')
.setDescription(`${initiator} and ${partner} successfully traded items!`)
.setTimestamp();
await i.update({ embeds: [successEmbed], components: [] });
}
});
collector.on('end', async (collected) => {
if (collected.size === 0) {
activeTrades.delete(tradeKey);
const timeoutEmbed = new EmbedBuilder()
.setColor(EMBED_COLORS.warning)
.setTitle('🔄 Trade Expired')
.setDescription(`${partner} didn't respond in time.`)
.setTimestamp();
await interaction.editReply({ embeds: [timeoutEmbed], components: [] });
}
});
},
};