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
- Advanced Features - Custom drivers, error handling, and more