188 lines
5.9 KiB
JavaScript
188 lines
5.9 KiB
JavaScript
/**
|
||
* 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 { logSecurity } = require('../services/debugLog');
|
||
const { enqueueSend } = require('../services/channelQueue');
|
||
|
||
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 identifier = subcommand === 'email'
|
||
? interaction.options.getString('email')
|
||
: interaction.options.getUser('user')?.tag || 'unknown';
|
||
logSecurity('Account lookup', interaction.user, `lookup: ${subcommand} → ${identifier}`, null, 0x0099ff).catch(() => {});
|
||
|
||
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 enqueueSend(channel, { 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
|
||
};
|