import {
Client,
GatewayIntentBits,
SlashCommandBuilder,
REST,
Routes,
ChatInputCommandInteraction,
Collection,
Events,
} from 'discord.js';
// ============================================
// Types
// ============================================
interface Command {
data: SlashCommandBuilder;
execute: (interaction: ChatInputCommandInteraction) => Promise<void>;
}
// ============================================
// Commands
// ============================================
const pingCommand: Command = {
data: new SlashCommandBuilder()
.setName('ping')
.setDescription('Check bot latency'),
async execute(interaction) {
const latency = Date.now() - interaction.createdTimestamp;
await interaction.reply(`π Pong! Latency: ${latency}ms`);
},
};
const echoCommand: Command = {
data: new SlashCommandBuilder()
.setName('echo')
.setDescription('Echo a message')
.addStringOption(option =>
option
.setName('message')
.setDescription('The message to echo')
.setRequired(true)
),
async execute(interaction) {
const message = interaction.options.getString('message', true);
await interaction.reply(`π’ ${message}`);
},
};
const userInfoCommand: Command = {
data: new SlashCommandBuilder()
.setName('userinfo')
.setDescription('Get information about a user')
.addUserOption(option =>
option
.setName('user')
.setDescription('The user to get info about')
.setRequired(false)
),
async execute(interaction) {
const user = interaction.options.getUser('user') ?? interaction.user;
const member = interaction.guild?.members.cache.get(user.id);
const embed = {
color: 0x5865F2,
title: `User Info: ${user.username}`,
thumbnail: { url: user.displayAvatarURL() },
fields: [
{ name: 'ID', value: user.id, inline: true },
{ name: 'Created', value: user.createdAt.toLocaleDateString(), inline: true },
{ name: 'Bot', value: user.bot ? 'Yes' : 'No', inline: true },
],
};
if (member) {
embed.fields.push(
{ name: 'Joined Server', value: member.joinedAt?.toLocaleDateString() ?? 'Unknown', inline: true },
{ name: 'Roles', value: member.roles.cache.size.toString(), inline: true }
);
}
await interaction.reply({ embeds: [embed] });
},
};
// ============================================
// Bot Setup
// ============================================
const commands = new Collection<string, Command>();
const commandList = [pingCommand, echoCommand, userInfoCommand];
commandList.forEach(cmd => commands.set(cmd.data.name, cmd));
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
],
});
// ============================================
// Event Handlers
// ============================================
client.once(Events.ClientReady, (c) => {
console.log(`β
Logged in as ${c.user.tag}`);
console.log(`π Serving ${c.guilds.cache.size} servers`);
});
client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isChatInputCommand()) return;
const command = commands.get(interaction.commandName);
if (!command) {
await interaction.reply({ content: 'Unknown command', ephemeral: true });
return;
}
try {
await command.execute(interaction);
} catch (error) {
console.error(`Error executing ${interaction.commandName}:`, error);
const reply = { content: 'There was an error executing this command!', ephemeral: true };
if (interaction.replied || interaction.deferred) {
await interaction.followUp(reply);
} else {
await interaction.reply(reply);
}
}
});
// ============================================
// Deploy Commands
// ============================================
async function deployCommands(clientId: string, guildId?: string) {
const rest = new REST().setToken(process.env.DISCORD_TOKEN!);
const commandData = commandList.map(cmd => cmd.data.toJSON());
try {
console.log('π Deploying slash commands...');
if (guildId) {
// Deploy to specific guild (instant)
await rest.put(Routes.applicationGuildCommands(clientId, guildId), { body: commandData });
console.log(`β
Deployed to guild ${guildId}`);
} else {
// Deploy globally (takes up to 1 hour)
await rest.put(Routes.applicationCommands(clientId), { body: commandData });
console.log('β
Deployed globally');
}
} catch (error) {
console.error('Failed to deploy commands:', error);
}
}
// ============================================
// Start Bot
// ============================================
const TOKEN = process.env.DISCORD_TOKEN;
const CLIENT_ID = process.env.DISCORD_CLIENT_ID;
const GUILD_ID = process.env.DISCORD_GUILD_ID; // Optional, for dev
if (!TOKEN || !CLIENT_ID) {
console.error('Missing DISCORD_TOKEN or DISCORD_CLIENT_ID');
process.exit(1);
}
// Deploy commands then start
deployCommands(CLIENT_ID, GUILD_ID).then(() => {
client.login(TOKEN);
});
export { client, commands };