⚠️ Work in progress. cord is in early development — APIs may change and not all features are stable yet.
A minimal Discord bot library built directly on top of the Discord API — no abstractions you didn't ask for.
import { Bot } from '@neozmmv/cord';
const bot = new Bot(process.env.TOKEN!);
bot.addCommand('ping', 'Replies with Pong!', async (ctx) => {
await ctx.sendMessage(`Pong! Hey ${ctx.user.username}`);
});
await bot.run();Most Discord libraries hide everything. cord gives you a clean API without taking away control.
- No manual command registration —
addCommandhandles it automatically on startup - No boilerplate — one file, one bot, done
- TypeScript-first — full autocomplete on
ctx, commands, and payloads - Direct API — built on raw WebSocket + REST, no hidden magic
- Event system — listen to any Discord Gateway event with full type safety
bun add @neozmmv/cord
# or
npm install @neozmmv/cordRequires Bun or Node.js 18+
1. Create a bot on the Discord Developer Portal
2. Set up your .env:
TOKEN=your_bot_token_here3. Create your bot:
import { Bot } from '@neozmmv/cord';
const bot = new Bot(process.env.TOKEN!);
bot.addCommand('ping', 'Check if the bot is alive', async (ctx) => {
await ctx.sendMessage('Pong!');
});
await bot.run();
console.log(`Online as ${bot.user?.username}`);Registers a slash command. Pass a guildId to register instantly in a specific server, or omit it for global registration (up to 1 hour to propagate).
// global command
bot.addCommand('help', 'Shows help', async (ctx) => {
await ctx.sendMessage('Here is the help menu...');
});
// guild-only (instant)
bot.addCommand('admin', 'Admin only', async (ctx) => {
await ctx.sendMessage('Admin panel');
}, process.env.GUILD_ID);Command names must be lowercase and can only contain letters and numbers.
bot
.addCommand('ping', 'Ping the bot', pingHandler)
.addCommand('help', 'Show help', helpHandler)
.addCommand('info', 'Server info', infoHandler);Every command handler receives a ctx object:
bot.addCommand('info', 'Get info', async (ctx) => {
ctx.commandName // "info"
ctx.user.username // who used the command
ctx.user.id // their Discord ID
ctx.guildId // server ID
ctx.channelId // channel ID
ctx.channel.name // channel name
ctx.locale // user's locale ("en-US", "pt-BR", ...)
await ctx.sendMessage('Hello!');
await ctx.sendMessage('Only you can see this!', { ephemeral: true });
await ctx.editMessage('Updated message!');
});Listen to any Discord Gateway event with bot.on(). Every event is fully typed — autocomplete works out of the box.
import { Bot, Event } from '@neozmmv/cord';
bot.on(Event.MESSAGE_CREATE, async (ctx) => {
if (ctx.content === '!hello') {
const msgId = await ctx.sendMessage(`Hello, ${ctx.author.global_name}!`);
await ctx.editMessage(msgId, 'Edited!');
}
});
bot.on(Event.GUILD_MEMBER_ADD, (ctx) => {
console.log(`New member: ${ctx.user.username}`);
});Multiple handlers for the same event are supported:
bot.on(Event.MESSAGE_CREATE, handlerOne);
bot.on(Event.MESSAGE_CREATE, handlerTwo); // both will be calledEvent.READY
Event.RESUMED
Event.CHANNEL_CREATE / UPDATE / DELETE / PINS_UPDATE
Event.GUILD_CREATE / UPDATE / DELETE
Event.GUILD_BAN_ADD / REMOVE
Event.GUILD_EMOJIS_UPDATE / STICKERS_UPDATE
Event.GUILD_MEMBER_ADD / UPDATE / REMOVE
Event.MESSAGE_CREATE / UPDATE / DELETE / DELETE_BULK
Event.MESSAGE_REACTION_ADD / REMOVE / REMOVE_ALL
Event.PRESENCE_UPDATE
Event.TYPING_START
Event.VOICE_STATE_UPDATE / SERVER_UPDATE
Event.INTERACTION_CREATE
Event.THREAD_CREATE / UPDATE / DELETE / MEMBER_UPDATEHandlers for Event.MESSAGE_CREATE receive a MessageContext with helper methods:
bot.on(Event.MESSAGE_CREATE, async (ctx) => {
ctx.content // message content
ctx.author.username // who sent the message
ctx.channelId // channel ID
ctx.guildId // server ID
const msgId = await ctx.sendMessage('Pong!');
await ctx.editMessage(msgId, 'Pong! (edited)');
});cord includes sensible defaults — Guilds, GuildMessages, MessageContent, and GuildMembers. You can customize:
import { Bot, Intent } from '@neozmmv/cord';
// use defaults
const bot = new Bot(process.env.TOKEN!);
// custom intents
const bot = new Bot(process.env.TOKEN!, [
Intent.Guilds,
Intent.GuildMessages,
Intent.GuildVoiceStates,
]);
// extend defaults
const bot = new Bot(process.env.TOKEN!, [
...Bot.DEFAULT_INTENTS,
Intent.GuildVoiceStates,
]);Some intents like
MessageContentandGuildMembersare privileged and must be enabled in the Discord Developer Portal.
cord connects directly to the Discord Gateway via WebSocket and manages the full lifecycle:
connect → HELLO → IDENTIFY → READY → register commands → listen for events
The heartbeat is handled automatically — your bot stays connected.
MIT