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 };