AeThex-Bot-Master/aethex-bot/commands/duel.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

176 lines
5.9 KiB
JavaScript

const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js');
const { getServerMode, getEmbedColor, EMBED_COLORS } = require('../utils/modeHelper');
const { updateStandaloneXp } = require('../utils/standaloneXp');
const activeDuels = new Map();
module.exports = {
data: new SlashCommandBuilder()
.setName('duel')
.setDescription('Challenge someone to a duel!')
.addUserOption(option =>
option.setName('opponent')
.setDescription('The user to challenge')
.setRequired(true)
)
.addIntegerOption(option =>
option.setName('bet')
.setDescription('XP to bet (0-100)')
.setRequired(false)
.setMinValue(0)
.setMaxValue(100)
),
async execute(interaction, supabase, client) {
const opponent = interaction.options.getUser('opponent');
const bet = interaction.options.getInteger('bet') || 0;
const challenger = interaction.user;
const mode = await getServerMode(supabase, interaction.guildId);
const guildId = interaction.guildId;
if (opponent.id === challenger.id) {
return interaction.reply({ content: "You can't duel yourself!", ephemeral: true });
}
if (opponent.bot) {
return interaction.reply({ content: "You can't duel bots!", ephemeral: true });
}
const duelKey = `${guildId}-${challenger.id}`;
if (activeDuels.has(duelKey)) {
return interaction.reply({ content: 'You already have an active duel!', ephemeral: true });
}
const embed = new EmbedBuilder()
.setColor(getEmbedColor(mode))
.setTitle('⚔️ Duel Challenge!')
.setDescription(`${challenger} challenges ${opponent} to a duel!`)
.addFields(
{ name: '💰 Bet', value: bet > 0 ? `${bet} XP` : 'No bet', inline: true },
{ name: '⏱️ Expires', value: 'In 60 seconds', inline: true }
)
.setTimestamp();
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId(`duel_accept_${challenger.id}_${opponent.id}`)
.setLabel('Accept')
.setStyle(ButtonStyle.Success),
new ButtonBuilder()
.setCustomId(`duel_decline_${challenger.id}_${opponent.id}`)
.setLabel('Decline')
.setStyle(ButtonStyle.Danger)
);
const message = await interaction.reply({
content: `${opponent}`,
embeds: [embed],
components: [row],
fetchReply: true
});
activeDuels.set(duelKey, { opponent: opponent.id, bet });
const collector = message.createMessageComponentCollector({
filter: i => i.user.id === opponent.id && i.customId.includes(challenger.id),
time: 60000,
max: 1
});
collector.on('collect', async (i) => {
activeDuels.delete(duelKey);
if (i.customId.startsWith('duel_decline')) {
const declineEmbed = new EmbedBuilder()
.setColor(EMBED_COLORS.error)
.setTitle('⚔️ Duel Declined')
.setDescription(`${opponent} declined the duel challenge.`)
.setTimestamp();
await i.update({ embeds: [declineEmbed], components: [] });
return;
}
const challengerRoll = Math.floor(Math.random() * 100) + 1;
const opponentRoll = Math.floor(Math.random() * 100) + 1;
let winner, loser, winnerRoll, loserRoll;
if (challengerRoll > opponentRoll) {
winner = challenger;
loser = opponent;
winnerRoll = challengerRoll;
loserRoll = opponentRoll;
} else if (opponentRoll > challengerRoll) {
winner = opponent;
loser = challenger;
winnerRoll = opponentRoll;
loserRoll = challengerRoll;
} else {
const tieEmbed = new EmbedBuilder()
.setColor(EMBED_COLORS.warning)
.setTitle('⚔️ It\'s a Tie!')
.setDescription(`Both rolled **${challengerRoll}**! Nobody wins.`)
.addFields(
{ name: `${challenger.username}`, value: `🎲 ${challengerRoll}`, inline: true },
{ name: `${opponent.username}`, value: `🎲 ${opponentRoll}`, inline: true }
)
.setTimestamp();
await i.update({ embeds: [tieEmbed], components: [] });
return;
}
if (bet > 0) {
if (mode === 'standalone') {
await updateStandaloneXp(supabase, winner.id, guildId, bet, winner.username);
} else if (supabase) {
try {
const { data: winnerProfile } = await supabase
.from('user_profiles')
.select('xp')
.eq('discord_id', winner.id)
.maybeSingle();
if (winnerProfile) {
await supabase
.from('user_profiles')
.update({ xp: (winnerProfile.xp || 0) + bet })
.eq('discord_id', winner.id);
}
} catch (e) {}
}
}
const resultEmbed = new EmbedBuilder()
.setColor(EMBED_COLORS.success)
.setTitle('⚔️ Duel Results!')
.setDescription(`🏆 **${winner.username}** wins the duel!`)
.addFields(
{ name: `${challenger.username}`, value: `🎲 Rolled: **${challengerRoll}**`, inline: true },
{ name: `${opponent.username}`, value: `🎲 Rolled: **${opponentRoll}**`, inline: true }
)
.setTimestamp();
if (bet > 0) {
resultEmbed.addFields({ name: '💰 Reward', value: `${winner.username} wins **${bet} XP**!` });
}
await i.update({ embeds: [resultEmbed], components: [] });
});
collector.on('end', async (collected) => {
if (collected.size === 0) {
activeDuels.delete(duelKey);
const timeoutEmbed = new EmbedBuilder()
.setColor(EMBED_COLORS.warning)
.setTitle('⚔️ Duel Expired')
.setDescription(`${opponent} didn't respond in time.`)
.setTimestamp();
await interaction.editReply({ embeds: [timeoutEmbed], components: [] });
}
});
},
};