/** * Account info command: look up website User by email or Discord ID, * show ephemeral embed with option to send transcript to account info channel. */ const { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js'); const { CONFIG } = require('../config'); const { mongoose } = require('../db-connection'); const User = mongoose.model('User'); const BUTTON_PREFIX = 'send_account_info_'; const MAX_CUSTOM_ID_LENGTH = 100; function buildAccountInfoEmbed(user, requestedBy = null) { const embed = new EmbedBuilder() .setTitle('Account Info') .setColor(CONFIG.EMBED_COLOR_INFO) .setTimestamp(); embed.addFields({ name: 'Email', value: user.email || '*not set*', inline: true }); embed.addFields({ name: 'Discord ID', value: user.discordID ? `<@${user.discordID}>` : '*not set*', inline: true }); embed.addFields({ name: 'Customer ID', value: user.customerId || '*not set*', inline: true }); const servers = user.servers || []; const serverOrder = user.serverOrder || []; const ordered = serverOrder.length ? serverOrder.map(id => servers.find(s => s._id && s._id.toString() === id) || servers[serverOrder.indexOf(id)]).filter(Boolean) : servers; if (ordered.length === 0) { embed.addFields({ name: 'Servers', value: '*No servers*', inline: false }); } else { ordered.forEach((server, i) => { const n = i + 1; embed.addFields({ name: `Server ${n} – Game`, value: server.game || '*not set*', inline: true }); embed.addFields({ name: `Server ${n} – IP`, value: server.ip || '*not set*', inline: true }); embed.addFields({ name: `Server ${n} – Port`, value: server.serverPort != null ? String(server.serverPort) : '*not set*', inline: true }); }); } if (requestedBy) { embed.setFooter({ text: `Requested by ${requestedBy}` }); } return embed; } async function handleAccountInfoCommand(interaction) { const subcommand = interaction.options.getSubcommand(); let user = null; if (subcommand === 'email') { const email = (interaction.options.getString('email') || '').trim().toLowerCase(); if (!email) { return interaction.reply({ content: 'Please provide an email.', ephemeral: true }); } user = await User.findOne({ email }).lean(); } else if (subcommand === 'discord') { const target = interaction.options.getUser('user'); if (!target) { return interaction.reply({ content: 'Please provide a Discord user.', ephemeral: true }); } user = await User.findOne({ discordID: target.id }).lean(); } if (!user) { return interaction.reply({ content: subcommand === 'email' ? 'No account found for that email.' : 'No account found for that Discord user/ID.', ephemeral: true }); } const embed = buildAccountInfoEmbed(user, interaction.user.tag); const components = []; if (CONFIG.ACCOUNT_INFO_CHANNEL_ID) { const safeEmail = (user.email || '').slice(0, 50); const safeDiscordId = (user.discordID || '').slice(0, 50); const customId = `${BUTTON_PREFIX}discord:${safeDiscordId}`; if (customId.length <= MAX_CUSTOM_ID_LENGTH) { components.push( new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId(customId) .setLabel('Send to account info channel') .setStyle(ButtonStyle.Secondary) ) ); } } await interaction.reply({ embeds: [embed], components, ephemeral: true }); } async function handleSendAccountInfoToChannel(interaction) { if (!interaction.isButton() || !interaction.customId.startsWith(BUTTON_PREFIX)) return false; const payload = interaction.customId.slice(BUTTON_PREFIX.length); const [type, value] = payload.includes(':') ? payload.split(':') : [payload, '']; let user = null; if (type === 'email') { const email = Buffer.from(value, 'base64').toString('utf8').toLowerCase(); user = await User.findOne({ email }).lean(); } else if (type === 'discord' && value) { user = await User.findOne({ discordID: value }).lean(); } if (!user) { await interaction.update({ content: 'Account no longer found.', components: [] }).catch(() => interaction.followUp({ content: 'Account no longer found.', ephemeral: true }) ); return true; } if (!CONFIG.ACCOUNT_INFO_CHANNEL_ID) { await interaction.update({ content: 'Account info channel is not configured.', components: [] }).catch(() => interaction.followUp({ content: 'Account info channel is not configured.', ephemeral: true }) ); return true; } const channel = await interaction.client.channels.fetch(CONFIG.ACCOUNT_INFO_CHANNEL_ID).catch(() => null); if (!channel) { await interaction.update({ content: 'Could not find account info channel.', components: [] }).catch(() => interaction.followUp({ content: 'Could not find account info channel.', ephemeral: true }) ); return true; } const embed = buildAccountInfoEmbed(user, `${interaction.user.tag} (from ticket)`); await channel.send({ embeds: [embed] }); await interaction.update({ content: 'Account info sent to account transcript channel.', components: [] }).catch(() => interaction.followUp({ content: 'Account info sent to account transcript channel.', ephemeral: true }) ); return true; } module.exports = { buildAccountInfoEmbed, handleAccountInfoCommand, handleSendAccountInfoToChannel, BUTTON_PREFIX };