Carbon

Listeners

Handling Discord events through webhooks and gateway

When you're building a Discord bot, you need to know when stuff happens - when someone sends a message, joins a server, or clicks a button. Discord sends these updates in two ways: through webhooks and through the Gateway. Let's look at how Carbon helps you handle both types easily.

The Two Types of Events

Discord has two main ways of telling your bot what's happening:

  1. Webhook Events: These are HTTP requests that Discord sends directly to your bot. They're great for handling things like:

    • Slash commands and component interactions
    • OAuth2 authorization grants
    • Application commands and message components
    • Premium subscription events These arrive as HTTP POST requests and might be slightly out of order.
  2. Gateway Events: These come through a WebSocket connection - think of it like a constant phone line between your bot and Discord. These events:

    • Arrive in real-time with strict ordering
    • Maintain session state and resumability
    • Require specific intents to be enabled
    • Include all real-time Discord updates

The key technical differences are:

  • Delivery Mechanism: Webhooks use HTTP POST requests, Gateway uses WebSocket frames
  • Ordering: Gateway events are strictly ordered, webhooks may arrive out of sequence
  • Latency: Gateway events arrive in real-time, webhooks have variable latency
  • State: Gateway maintains session state, webhooks are stateless
  • Reliability: Gateway includes built-in reconnection, webhooks may need manual retry logic
  • Scale: Webhooks can scale horizontally, Gateway requires persistent connections
  • Rate Limiting: Different rate limit rules apply to each type
  • Authentication: Webhooks use signatures, Gateway uses token auth

If you want to learn more about setting up Gateway connections, check out our Gateway guide.

Creating Listeners in Carbon

Carbon makes it super easy to handle both types of events using the same pattern. All listeners extend from the BaseListener class that provides type safety and consistent interfaces:

abstract class BaseListener {
	abstract readonly type: ListenerEventType
	abstract handle(
		data: ListenerEventData[this["type"]],
		client: Client
	): Promise<void>
}

Let's look at some examples:

Handling Bot Installation

Want to do something when someone adds your bot to their server? Here's how:

src/events/authorized.ts
import { 
	ListenerEvent,
	type Client,
	ApplicationAuthorizedListener,
	type ListenerEventData,
	ApplicationIntegrationType
} from "@buape/carbon"
 
export class ApplicationAuthorized extends ApplicationAuthorizedListener {
	async handle(
		data: ListenerEventData[typeof ListenerEvent.ApplicationAuthorized],
		client: Client
	) {
		if (data.integration_type === ApplicationIntegrationType.GuildInstall) {
			console.log(`Bot was added to ${data.guild?.name}`)
			// Maybe set up some initial server settings here
			
			// Example: Send welcome message
 
		} else {
			console.log(`${data.user.username} authorized the app`)
			// Handle other authorization types
		}
	}
}

Handling Gateway Events

Gateway events are where the fun stuff happens. Here are a couple of common examples:

When Your Bot Starts Up

src/events/ready.ts
import { 
	ListenerEvent,
	type Client,
	ReadyListener,
	type ListenerEventData 
} from "@buape/carbon"
 
export class Ready extends ReadyListener {
	async handle(
		data: ListenerEventData[typeof ListenerEvent.Ready],
		client: Client
	) {
		console.log(`Ready! Logged in as ${data.user.username}`)
		console.log(`Serving ${data.guilds.length} servers`)
		
		// Set up any initialization tasks here
		await registerCommands(client);
		await setupPeriodicTasks(client);
	}
}

Handling Messages

Want to do something when people send messages? Here's how:

src/events/messageCreate.ts
import { 
	ListenerEvent,
	type Client,
	MessageCreateListener,
	type ListenerEventData 
} from "@buape/carbon"
 
export class MessageCreate extends MessageCreateListener {
	async handle(
		data: ListenerEventData[typeof ListenerEvent.MessageCreate],
		client: Client
	) {
		// Don't respond to other bots!
		if (data.author.bot) return;
 
		const { content, author, guild } = data;
		
		if (guild && content) {
			// Handle the message in a guild context
			console.log(`${author.username} said: ${content} in ${guild.name}`);
			
			// Example: Command handling
			if (content.startsWith('!')) {
				const [command, ...args] = content.slice(1).split(' ');
				await handleCommand(command, args, data, client);
			}
		}
	}
}

Adding Your Listeners

Once you've created your listeners, you need to tell Carbon about them. Just add them to your client setup:

src/index.ts
import { Client } from "@buape/carbon"
import { Ready } from "./events/ready"
import { MessageCreate } from "./events/messageCreate"
import { ApplicationAuthorized } from "./events/authorized"
 
const client = new Client(
	{
		baseUrl: process.env.BASE_URL,
		deploySecret: process.env.DEPLOY_SECRET,
		clientId: process.env.DISCORD_CLIENT_ID,
		publicKey: process.env.DISCORD_PUBLIC_KEY,
		token: process.env.DISCORD_BOT_TOKEN
	},
	{
		commands: [], // Your slash commands go here
		listeners: [
			new Ready(),
			new MessageCreate(),
			new ApplicationAuthorized()
		]
	}
)

Events You Can Listen For

Carbon supports all of Discord's events. Here are some of the most common ones:

Gateway Events

  • Server events: GuildCreate, GuildDelete, GuildUpdate
  • Member events: GuildMemberAdd, GuildMemberRemove, GuildMemberUpdate
  • Message events: MessageCreate, MessageUpdate, MessageDelete
  • Channel events: ChannelCreate, ChannelUpdate, ChannelDelete
  • Voice events: VoiceStateUpdate, VoiceServerUpdate
  • Interaction events: InteractionCreate

Webhook Events

  • Application events: ApplicationAuthorized
  • Premium events: EntitlementCreate, EntitlementUpdate, EntitlementDelete
  • Quest events: QuestUserEnrollment

Each event type comes with its own data structure defined in the ListenerEventData type, which provides full type safety and autocompletion in your IDE. The event data includes everything you need to know about what happened, properly typed and easily accessible.

Edit on GitHub

Last updated on

On this page