Skip to main content
Version: Next

Discord.js Integration

CommandKit Queue provides seamless integration with Discord.js through the @discordjs/brokers package and Redis PubSub. This allows you to send messages between different Discord.js shards or even different Discord.js applications.

Prerequisites

Install the required dependencies:

npm install @commandkit/queue @discordjs/brokers ioredis

Basic Setup

1. Create a Redis Connection

First, you need a Redis instance running. You can use a local Redis server or a cloud service like Redis Cloud.

import Redis from 'ioredis';

// Local Redis
const redis = new Redis();

// Or with configuration
const redis = new Redis({
host: 'localhost',
port: 6379,
password: 'your-password',
db: 0,
});

// Cloud Redis (example with Redis Cloud)
const redis = new Redis({
host: 'your-redis-host.redis.cloud.com',
port: 6379,
password: 'your-redis-password',
tls: {},
});

2. Set Up the Broker and Driver

import { setDriver } from '@commandkit/queue';
import { RedisPubSubDriver } from '@commandkit/queue/discordjs';
import { PubSubRedisBroker } from '@discordjs/brokers';
import Redis from 'ioredis';

// Create Redis connection
const redis = new Redis();

// Create the broker
const broker = new PubSubRedisBroker(redis);

// Create the driver
const driver = new RedisPubSubDriver(broker);

// Set the driver
setDriver(driver);

Type-Safe Events

You can define typed events for better TypeScript support:

interface QueueEvents {
'user-updates': {
userId: string;
action: 'login' | 'logout' | 'register';
timestamp: number;
};
'guild-events': {
guildId: string;
event: 'member-join' | 'member-leave' | 'role-update';
data: any;
};
analytics: {
event: string;
data: Record<string, any>;
timestamp: number;
};
}

// Create a typed driver
const driver = new RedisPubSubDriver<QueueEvents>(broker);
setDriver(driver);

Sending Messages

Send messages to specific topics:

import { send } from '@commandkit/queue';

// Send user events
await send('user-updates', {
userId: '123456789',
action: 'login',
timestamp: Date.now(),
});

// Send guild events
await send('guild-events', {
guildId: '987654321',
event: 'member-join',
data: { memberId: '123456789', username: 'JohnDoe' },
});

// Send analytics
await send('analytics', {
event: 'command-executed',
data: { command: 'ping', userId: '123456789' },
timestamp: Date.now(),
});

Receiving Messages

Subscribe to messages from topics:

import { receive } from '@commandkit/queue';

// Handle user updates
await receive('user-updates', (message) => {
console.log(
`User ${message.userId} ${message.action} at ${new Date(message.timestamp)}`,
);

// Update user status in your application
updateUserStatus(message.userId, message.action);
});

// Handle guild events
await receive('guild-events', (message) => {
switch (message.event) {
case 'member-join':
console.log(
`Member ${message.data.memberId} joined guild ${message.guildId}`,
);
break;
case 'member-leave':
console.log(
`Member ${message.data.memberId} left guild ${message.guildId}`,
);
break;
}
});

// Handle analytics
await receive('analytics', async (message) => {
await logAnalyticsEvent(message.event, message.data);
});

Discord.js Integration Examples

Cross-Shard Communication

Send messages between different shards of your Discord.js application:

// In shard 1
await send('shard-communication', {
fromShard: 0,
toShard: 1,
type: 'user-status-update',
data: { userId: '123456789', status: 'online' },
});

// In shard 1 (receiving)
await receive('shard-communication', (message) => {
if (message.toShard === 0) {
console.log(`Received from shard ${message.fromShard}:`, message.data);
}
});

Multi-Bot Communication

Send messages between different Discord.js bots:

// In bot A
await send('bot-communication', {
fromBot: 'bot-a',
toBot: 'bot-b',
type: 'user-data-request',
data: { userId: '123456789' },
});

// In bot B
await receive('bot-communication', async (message) => {
if (message.toBot === 'bot-b') {
const userData = await getUserData(message.data.userId);

await send('bot-communication', {
fromBot: 'bot-b',
toBot: 'bot-a',
type: 'user-data-response',
data: userData,
});
}
});

Real-time Updates

Send real-time updates to connected clients:

// When a user joins a voice channel
await send('voice-updates', {
userId: '123456789',
guildId: '987654321',
channelId: '111222333',
action: 'join',
timestamp: Date.now(),
});

// Handle voice updates
await receive('voice-updates', (message) => {
// Update voice channel status
updateVoiceChannelStatus(
message.guildId,
message.channelId,
message.userId,
message.action,
);

// Notify other users
notifyVoiceChannelUsers(message.guildId, message.channelId, message);
});

Error Handling

Handle connection and message processing errors:

// Handle Redis connection errors
redis.on('error', (error) => {
console.error('Redis connection error:', error);
});

redis.on('connect', () => {
console.log('Connected to Redis');
});

// Handle broker errors
broker.on('error', (error) => {
console.error('Broker error:', error);
});

// Handle message processing errors
await receive('user-updates', async (message) => {
try {
await processUserUpdate(message);
} catch (error) {
console.error('Failed to process user update:', error);

// Optionally retry or send to dead letter queue
await send('failed-messages', {
originalTopic: 'user-updates',
message,
error: error.message,
timestamp: Date.now(),
});
}
});

Performance Considerations

Connection Pooling

For high-traffic applications, consider using connection pooling:

import Redis from 'ioredis';

// Create a connection pool
const redis = new Redis({
host: 'localhost',
port: 6379,
maxRetriesPerRequest: 3,
retryDelayOnFailover: 100,
enableReadyCheck: false,
maxLoadingTimeout: 10000,
});

Message Batching

For high-volume scenarios, consider batching messages:

// Batch multiple updates
const updates = [
{ userId: '123', action: 'login' },
{ userId: '456', action: 'logout' },
{ userId: '789', action: 'login' },
];

// Send as a batch
await send('user-updates-batch', {
updates,
timestamp: Date.now(),
});

// Process batch
await receive('user-updates-batch', async (message) => {
for (const update of message.updates) {
await processUserUpdate(update);
}
});

Cleanup

Always clean up resources when shutting down:

import { setDriver } from '@commandkit/queue';

// Cleanup function
async function cleanup() {
try {
// Close the driver
const driver = getDriver(); // You'll need to implement this
if (driver && driver.close) {
await driver.close();
}

// Close Redis connection
await redis.quit();

console.log('Queue cleanup completed');
} catch (error) {
console.error('Error during cleanup:', error);
}
}

// Handle graceful shutdown
process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);

Next Steps