Hazel

HazelBotClient

The main bot service for event handling and message operations

HazelBotClient is the main service for building Hazel bots. It provides event handlers, message operations, and slash command support.

Getting the Client

import { Effect } from "effect"
import { HazelBotClient } from "@hazel/bot-sdk"

const program = Effect.gen(function* () {
	const bot = yield* HazelBotClient
	// Use bot here
})

Event Handlers

onMessage

Register a handler for new messages.

yield *
	bot.onMessage((message) =>
		Effect.gen(function* () {
			yield* Effect.log(`New message: ${message.content}`)
		}),
	)

Message properties:

PropertyTypeDescription
idMessageIdUnique message ID
contentstringMessage text content
channelIdChannelIdChannel where sent
authorIdUserIdUser who sent it
createdAtstringISO timestamp
updatedAtstring | nullLast edit time
replyToMessageIdMessageId | nullParent message if reply
threadChannelIdChannelId | nullThread channel if in thread

onMessageUpdate

Register a handler for message edits.

yield *
	bot.onMessageUpdate((message) =>
		Effect.gen(function* () {
			yield* Effect.log(`Message edited: ${message.content}`)
		}),
	)

onMessageDelete

Register a handler for message deletions.

yield *
	bot.onMessageDelete((message) =>
		Effect.gen(function* () {
			yield* Effect.log(`Message deleted: ${message.id}`)
		}),
	)

onChannelCreated

Register a handler for new channels.

yield *
	bot.onChannelCreated((channel) =>
		Effect.gen(function* () {
			yield* Effect.log(`New channel: ${channel.name}`)
		}),
	)

onChannelUpdated

Register a handler for channel updates.

yield *
	bot.onChannelUpdated((channel) =>
		Effect.gen(function* () {
			yield* Effect.log(`Channel updated: ${channel.name}`)
		}),
	)

onChannelDeleted

Register a handler for channel deletions.

yield *
	bot.onChannelDeleted((channel) =>
		Effect.gen(function* () {
			yield* Effect.log(`Channel deleted: ${channel.id}`)
		}),
	)

onChannelMemberAdded

Register a handler for members joining a channel.

yield *
	bot.onChannelMemberAdded((member) =>
		Effect.gen(function* () {
			yield* Effect.log(`Member joined: ${member.userId}`)
		}),
	)

onChannelMemberRemoved

Register a handler for members leaving a channel.

yield *
	bot.onChannelMemberRemoved((member) =>
		Effect.gen(function* () {
			yield* Effect.log(`Member left: ${member.userId}`)
		}),
	)

onCommand

Register a handler for a slash command. See Commands for details.

yield *
	bot.onCommand(MyCommand, (ctx) =>
		Effect.gen(function* () {
			yield* bot.message.send(ctx.channelId, `Hello, ${ctx.args.name}!`)
		}),
	)

Message Operations

bot.message.send

Send a message to a channel.

const message = yield * bot.message.send(channelId, "Hello!")

With options:

const message =
	yield *
	bot.message.send(channelId, "Reply!", {
		replyToMessageId: parentMessageId, // Optional: reply to message
		threadChannelId: threadId, // Optional: send in thread
		attachmentIds: [attachmentId], // Optional: attachments
	})

bot.message.reply

Reply to a message.

yield * bot.message.reply(message, "Got it!")

bot.message.update

Edit a message.

yield * bot.message.update(message, "Updated content")

bot.message.delete

Delete a message by ID.

yield * bot.message.delete(messageId)

bot.message.react

React to a message with an emoji.

yield * bot.message.react(message, "👍")

Typing Indicators

bot.typing.start

Show a typing indicator.

const indicator = yield * bot.typing.start(channelId, channelMemberId)

bot.typing.stop

Hide a typing indicator.

yield * bot.typing.stop(typingIndicatorId)

Channel Operations

bot.channel.update

Update a channel's name or description.

yield *
	bot.channel.update(channel, {
		name: "new-name",
		description: "New description",
	})

Utilities

bot.withErrorHandler

Wrap a command handler with error handling. Catches errors, logs them, and sends a user-friendly message.

yield *
	bot.onCommand(MyCommand, (ctx) =>
		Effect.gen(function* () {
			// Handler logic
		}).pipe(bot.withErrorHandler(ctx)),
	)

bot.start

Start the bot. Must be called after registering all handlers.

yield * bot.start

bot.getAuthContext

Get the bot's authentication context.

const auth = yield * bot.getAuthContext
yield * Effect.log(`Bot ID: ${auth.botId}`)
yield * Effect.log(`Bot User ID: ${auth.userId}`)

Rate Limiting

All message operations are automatically rate-limited to 10 per second. This prevents hitting API limits and ensures reliable delivery.

Streaming Operations

The SDK provides two streaming APIs: a low-level bot.stream for full control and a high-level bot.ai for AI model integration.

bot.stream.create

Create a low-level stream session for real-time message updates.

const session =
	yield *
	bot.stream.create(channelId, {
		loading: {
			title: "Processing...",
			description: "Please wait",
		},
	})

StreamSession methods:

MethodDescription
appendText(text)Append text to the current message
setText(text)Replace the entire message text
setProgress(progress)Update the loading progress indicator
setData(data)Set structured data on the message
startThinking(label?)Add a thinking/reasoning step
startToolCall(name, input?)Add a tool call step with name and input
updateStepContent(stepId, content)Update a step's content
completeStep(stepId, output?)Mark a step as complete with optional output
complete()Finalize the stream and persist the message
fail(error)Mark the stream as failed with an error

bot.ai.stream

Create an AI-aware stream session with automatic chunk processing.

const session =
	yield *
	bot.ai.stream(channelId, {
		model: "claude-3.5-sonnet",
		showThinking: true,
		showToolCalls: true,
		loading: {
			title: "Thinking...",
		},
	})

AIStreamOptions:

OptionTypeDescription
modelstringAI model identifier for display
showThinkingbooleanShow extended thinking steps (default: false)
showToolCallsbooleanShow tool call steps (default: false)
loadingobjectLoading state with title and description

AIStreamSession methods:

All StreamSession methods plus:

MethodDescription
processChunk(chunk)Process a single AI content chunk
processStream(stream)Process an entire stream of chunks

AIContentChunk types:

TypePropertiesDescription
texttext: stringText content to append
thinkingthinking: stringExtended thinking content
tool_callname: string, input?: unknownTool invocation
tool_resulttoolCallId: string, result: unknownTool execution result

bot.ai.withErrorHandler

Wrap an AI streaming operation with error handling that properly fails the stream on error.

yield * myOperation.pipe(bot.ai.withErrorHandler(ctx, session))

See AI Streaming for complete documentation and examples.

On this page