Files
BattleBot/src/commands/CommandHandler.js
VinceC 93679f8828 summary
small update for bot output
2025-01-04 11:22:29 -06:00

398 lines
15 KiB
JavaScript

const { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } = require('discord.js');
const EmbedBuilders = require('../utils/embedBuilders');
class CommandHandler {
constructor(playerService, supabase, logger, serverRegistrationService, subscriptionCommands) {
this.playerService = playerService;
this.supabase = supabase;
this.logger = logger;
this.serverRegistrationService = serverRegistrationService;
this.subscriptionCommands = subscriptionCommands;
}
async handleCommand(interaction) {
try {
switch (interaction.commandName) {
case 'register_server':
await this.handleRegisterServer(interaction);
break;
case 'ping':
await interaction.editReply({ content: 'Pong!', ephemeral: true });
break;
case 'finduser':
await this.handleFindUser(interaction);
break;
case 'matchhistory':
await this.handleMatchHistory(interaction);
break;
case 'subscribe':
await this.subscriptionCommands.handleSubscribe(interaction);
break;
case 'unsubscribe':
await this.subscriptionCommands.handleUnsubscribe(interaction);
break;
case 'list_subscriptions':
await this.subscriptionCommands.handleListSubscriptions(interaction);
break;
case 'findteam':
await this.handleFindTeam(interaction);
break;
default:
await interaction.editReply({
content: '❌ Unknown command',
ephemeral: true
});
}
} catch (error) {
this.logger.error('Command handling error:', {
command: interaction.commandName,
error: error.message,
stack: error.stack
});
try {
await interaction.editReply({
content: '❌ An error occurred while processing your command.',
ephemeral: true
});
} catch (followUpError) {
this.logger.error('Failed to send error response:', {
error: followUpError.message,
originalError: error.message
});
}
}
}
async handleRegisterServer(interaction) {
try {
// Check for admin permissions
if (!interaction.member.permissions.has('ADMINISTRATOR')) {
await interaction.editReply({
content: '❌ You need administrator permissions to register this server.',
ephemeral: true
});
return;
}
const guildId = interaction.guildId;
const serverName = interaction.guild.name;
this.logger.debug('Starting server registration', {
guildId,
serverName,
timestamp: new Date().toISOString()
});
// Attempt the database operation
const result = await this.serverRegistrationService.registerServer(guildId, serverName);
// Log the result
this.logger.debug('Server registration result received', {
status: result.status,
serverId: result.server?.id,
guildId,
timestamp: new Date().toISOString()
});
// Prepare response message based on status
let message;
switch (result.status) {
case 'created':
message = '✅ Server successfully registered! You can now use subscription commands.';
break;
case 'updated':
message = '✅ Server information has been updated!';
break;
case 'exists':
message = '✅ This server is already registered and ready to use subscription commands!';
break;
default:
message = '❌ An unexpected error occurred during registration.';
}
await interaction.editReply({
content: message,
ephemeral: true
});
// Log the successful operation
this.logger.info('Server registration completed', {
status: result.status,
guildId,
serverId: result.server.id,
timestamp: new Date().toISOString()
});
} catch (error) {
this.logger.error('Error in handleRegisterServer:', {
error: error.message,
stack: error.stack,
guildId: interaction.guildId,
serverName: interaction.guild?.name
});
// Send error message to user
const errorMessage = error.message === 'Invalid guildId provided' || error.message === 'Invalid serverName provided'
? '❌ Invalid server information provided.'
: '❌ An error occurred while registering the server. Please try again later.';
await interaction.editReply({
content: errorMessage,
ephemeral: true
});
throw error;
}
}
async handleFindUser(interaction) {
try {
const username = interaction.options.getString('username');
const gameFilter = interaction.options.getString('game');
// Input validation
if (!username || typeof username !== 'string') {
await interaction.editReply({
content: '❌ Invalid username provided.'
});
return;
}
// Enhanced sanitization
const sanitizedUsername = username
.replace(/[^a-zA-Z0-9\s\-_.]/g, '')
.trim()
.slice(0, 100);
if (!sanitizedUsername) {
await interaction.editReply({
content: '❌ Username must contain valid characters (letters, numbers, spaces, hyphens, underscores, or periods).'
});
return;
}
// Log sanitized input for monitoring
this.logger.debug('User search input:', {
original: username,
sanitized: sanitizedUsername,
game: gameFilter,
userId: interaction.user.id,
timestamp: new Date().toISOString()
});
const userData = await this.playerService.findUserByUsername(sanitizedUsername);
if (!userData || !userData.success) {
await interaction.editReply({
content: '❌ User not found or an error occurred while fetching data.'
});
return;
}
const playerData = typeof userData.player_data === 'string'
? JSON.parse(userData.player_data)
: userData.player_data;
// Check if the user is on a team for the specified game
const isOnTeam = playerData.teams && Object.values(playerData.teams)
.some(team => team.game_name === gameFilter);
// Check if the user has played the specified game
const hasPlayedGame = playerData.stats?.games &&
Object.keys(playerData.stats.games)
.some(game => game.toLowerCase() === gameFilter.toLowerCase());
if (!hasPlayedGame) {
const teamMessage = isOnTeam ?
`\nThey are on a team for ${gameFilter} but haven't played any matches yet.` :
'';
// Create a basic embed with user info
const basicEmbed = new EmbedBuilder()
.setTitle(`User: ${playerData.username || 'Unknown'}`)
.setDescription(playerData.profile?.bio || 'No bio available')
.setColor('#0099ff');
// Add avatar if available
if (playerData.profile?.avatar) {
basicEmbed.setThumbnail(
`https://www.vrbattles.gg/assets/uploads/profile/${playerData.profile.avatar}`
);
}
// Add badge image if available
if (playerData.profile?.current_level_badge) {
const badgeImageUrl = this.playerService.getBadgeImageUrl(playerData.profile.current_level_badge);
basicEmbed.setImage(badgeImageUrl);
} else {
basicEmbed.setImage('https://www.vrbattles.gg/assets/images/Qubi/vrwearcubesatad.png');
}
// Add basic profile fields
const profileFields = [];
const profile = playerData.profile || {};
if (profile.country) profileFields.push({ name: 'Country', value: profile.country, inline: true });
if (profile.level) profileFields.push({ name: 'Level', value: profile.level.toString(), inline: true });
if (profile.xp) profileFields.push({ name: 'Total XP', value: profile.xp.toString(), inline: true });
if (profile.prestige) profileFields.push({ name: 'Prestige', value: profile.prestige.toString(), inline: true });
if (profileFields.length > 0) {
basicEmbed.addFields(profileFields);
}
// Add message about no game stats
basicEmbed.addFields({
name: `${gameFilter} Status`,
value: `❌ No match history found for ${gameFilter}${teamMessage}`
});
const row = this.createActionRow(playerData.username);
await interaction.editReply({
embeds: [basicEmbed],
components: [row]
});
return;
}
const embed = EmbedBuilders.createUserEmbed(playerData, gameFilter, this.playerService);
const row = this.createActionRow(playerData.username);
await interaction.editReply({
embeds: [embed],
components: [row]
});
} catch (error) {
this.logger.error('Error in handleFindUser:', {
error: error.message,
username: interaction.options.getString('username'),
game: interaction.options.getString('game'),
userId: interaction.user.id,
timestamp: new Date().toISOString()
});
await interaction.editReply({
content: '❌ An error occurred while searching for the user.'
});
}
}
async handleMatchHistory(interaction) {
try {
const username = interaction.options.getString('username');
const gameFilter = interaction.options.getString('game');
const userData = await this.playerService.findUserByUsername(username);
if (!userData || !userData.success) {
await interaction.editReply({
content: '❌ User not found or an error occurred while fetching data.',
});
return;
}
const playerData = typeof userData.player_data === 'string'
? JSON.parse(userData.player_data)
: userData.player_data;
// Extract matches from player data
const matches = Object.values(playerData.matches || {})
.filter(match => !gameFilter || match.game_name.toLowerCase() === gameFilter.toLowerCase())
.sort((a, b) => new Date(b.start_time) - new Date(a.start_time))
.slice(0, 10);
const embed = EmbedBuilders.createMatchHistoryEmbed(playerData, matches);
const row = EmbedBuilders.createActionRow(playerData.username, this.playerService);
await interaction.editReply({
embeds: [embed],
components: [row],
});
} catch (error) {
this.logger.error('Error in handleMatchHistory:', {
error: error.message,
username: interaction.options.getString('username'),
timestamp: new Date().toISOString()
});
throw error;
}
}
async handleFindTeam(interaction) {
try {
const teamName = interaction.options.getString('teamname');
const gameFilter = interaction.options.getString('game');
// Input validation
if (!teamName || typeof teamName !== 'string') {
await interaction.editReply({
content: '❌ Invalid team name provided.',
ephemeral: false
});
return;
}
// Enhanced sanitization
const sanitizedTeamName = teamName
.replace(/[^a-zA-Z0-9\s\-_.]/g, '')
.trim()
.slice(0, 100);
if (!sanitizedTeamName) {
await interaction.editReply({
content: '❌ Team name must contain valid characters (letters, numbers, spaces, hyphens, underscores, or periods).',
ephemeral: false
});
return;
}
// Log sanitized input for monitoring
this.logger.debug('Team search input:', {
original: teamName,
sanitized: sanitizedTeamName,
game: gameFilter,
userId: interaction.user.id,
timestamp: new Date().toISOString()
});
const teamData = await this.playerService.findTeamByName(sanitizedTeamName, gameFilter);
if (!teamData || !teamData.success || !teamData.teams || teamData.teams.length === 0) {
await interaction.editReply({
content: '❌ No teams found matching your search criteria.',
ephemeral: false
});
return;
}
const embeds = EmbedBuilders.createTeamEmbed(teamData.teams);
await interaction.editReply({
embeds: embeds,
components: [],
ephemeral: false
});
} catch (error) {
this.logger.error('Error in handleFindTeam:', {
error: error.message,
teamName: interaction.options.getString('teamname'),
game: interaction.options.getString('game'),
userId: interaction.user.id,
timestamp: new Date().toISOString()
});
await interaction.editReply({
content: '❌ An error occurred while searching for teams.',
ephemeral: false
});
}
}
createActionRow(username) {
return new ActionRowBuilder().addComponents(
new ButtonBuilder()
.setLabel('🔵 View Profile')
.setStyle(ButtonStyle.Link)
.setURL(`https://www.vrbattles.gg/profile/${username}`)
);
}
}
module.exports = CommandHandler;