Custom Event Emitters
Custom Event Emitters
This guide explains how to create and use custom event emitters with the bot's event system.
Table of Contents
- Introduction
- Adding a Custom Event Emitter
- Defining Custom Event Types
- Creating Event Handlers
- Emitting Custom Events
- Complete Example
- Advanced Usage
Introduction
The bot's event system supports any event emitter that follows the Node.js EventEmitter pattern. This allows you to create custom event emitters for specialized functionality, such as:
- Database event notifications
- API webhooks
- Inter-service communication
- Custom bot features
Adding a Custom Event Emitter
To add a custom event emitter:
- Create a new EventEmitter instance in your DiscordBot class
- Define event types in
eventTypes.ts - Create event handlers
Step 1: Add the Event Emitter
// src/utils/structures/DiscordBot.ts
import { Client, Options, Partials } from "discord.js";
import { EventEmitter } from 'events';
export default class DiscordBot extends Client {
// ...existing properties
// Add your custom event emitter
public databaseEvents: EventEmitter = new EventEmitter();
// ...rest of class
}
Defining Custom Event Types
For type safety, you need to define your event types in src/utils/types/eventTypes.ts.
// src/utils/types/eventTypes.ts
// Define your custom event types
interface DatabaseEvents {
userUpdate: [userId: string, newData: UserData];
documentDelete: [collectionName: string, documentId: string];
connectionChange: [status: 'connected' | 'disconnected', timestamp: number];
}
export interface EventTypes {
discord: ClientEvents;
cluster: ClusterClientEvents<DiscordBot>;
// Add your custom events
databaseEvents: DatabaseEvents;
[key: string]: any;
}
Creating Event Handlers
Create event handlers in a new directory matching your emitter name:
// src/events/databaseEvents/userUpdate.ts
import { eventFile } from "../../utils/types/eventTypes.js";
export const data = eventFile({
name: "userUpdate",
eventGetter: "databaseEvents", // Match your emitter name
execute: async (client, userId, newData) => {
client.logger.info(`User ${userId} was updated with new data`);
// You can access the typed parameters
if (newData.hasVerifiedEmail) {
// Do something with the user data
}
}
});
Emitting Custom Events
You can now emit events from anywhere in your code:
// Example from a database service
async function updateUserInDatabase(userId, newData) {
// Update user in database
await db.users.update(userId, newData);
// Emit event to notify listeners
client.databaseEvents.emit('userUpdate', userId, newData);
}
Complete Example
Here's a complete example of adding a system status emitter:
- First, add the emitter to your bot class:
// src/utils/structures/DiscordBot.ts
import { EventEmitter } from 'events';
export default class DiscordBot extends Client {
// ... other properties
public systemStatus: EventEmitter = new EventEmitter();
}
- Define the event types:
// src/utils/types/eventTypes.ts
interface SystemStatusEvents {
cpuWarning: [usagePercent: number, processInfo: ProcessInfo];
memoryWarning: [usagePercent: number, freeMemory: number];
serviceStatus: [serviceName: string, status: 'up' | 'down' | 'degraded'];
}
export interface EventTypes {
discord: ClientEvents;
cluster: ClusterClientEvents<DiscordBot>;
systemStatus: SystemStatusEvents;
[key: string]: any;
}
- Create an event handler:
// src/events/systemStatus/memoryWarning.ts
import { eventFile } from "../../utils/types/eventTypes.js";
export const data = eventFile({
name: "memoryWarning",
eventGetter: "systemStatus",
execute: async (client, usagePercent, freeMemory) => {
if (usagePercent > 90) {
// High memory usage alert
client.logger.warn(`CRITICAL: Memory usage at ${usagePercent}%!`);
// Notify admin channel
const adminChannel = await client.channels.fetch('ADMIN_CHANNEL_ID');
if (adminChannel?.isTextBased()) {
await adminChannel.send({
embeds: [{
title: '⚠️ Memory Warning',
description: `Memory usage is at ${usagePercent}%.\nFree memory: ${(freeMemory / 1024 / 1024).toFixed(2)} MB`,
color: 0xFF0000
}]
});
}
} else {
// Just log normal warnings
client.logger.warn(`Memory usage at ${usagePercent}%`);
}
}
});
- Set up a monitoring system that emits events:
// src/utils/monitoring/memoryMonitor.ts
import os from 'os';
export function startMemoryMonitoring(client, checkInterval = 60000) {
// Check memory usage every minute
setInterval(() => {
const totalMem = os.totalmem();
const freeMem = os.freemem();
const usedMem = totalMem - freeMem;
const percentUsed = Math.round((usedMem / totalMem) * 100);
// Emit warning events based on usage
if (percentUsed > 70) {
client.systemStatus.emit('memoryWarning', percentUsed, freeMem);
}
}, checkInterval);
}
Advanced Usage
Using TypedEventEmitter
For better type safety, you can create a typed event emitter:
// src/utils/structures/TypedEventEmitter.ts
import { EventEmitter } from 'events';
export class TypedEventEmitter<Events extends Record<string, any[]>> extends EventEmitter {
public on<E extends keyof Events>(event: E, listener: (...args: Events[E]) => void): this {
return super.on(event as string, listener);
}
public once<E extends keyof Events>(event: E, listener: (...args: Events[E]) => void): this {
return super.once(event as string, listener);
}
public emit<E extends keyof Events>(event: E, ...args: Events[E]): boolean {
return super.emit(event as string, ...args);
}
}
// Usage
interface MyEvents {
hello: [name: string];
countChange: [newCount: number, oldCount: number];
}
const emitter = new TypedEventEmitter<MyEvents>();
// Fully typed
emitter.on('hello', (name) => {
console.log(`Hello, ${name}!`);
});
emitter.emit('hello', 'World'); // Correct
// emitter.emit('hello', 123); // Type error
// emitter.emit('nonexistent'); // Type error
