Tutorial
Build a Task Bot from scratch with the Hazel Bot SDK
In this tutorial, you'll build a Task Bot that helps teams manage tasks directly in Hazel. By the end, your bot will be able to:
- Respond to messages mentioning "task"
- Handle
/task addand/task listslash commands - Send confirmation messages and react with checkmarks
- Handle errors gracefully with user-friendly messages
What You'll Learn
- Project Setup - Initialize a bot project with proper configuration
- Message Handling - Listen for and respond to messages
- Slash Commands - Create type-safe commands with arguments
- Message Operations - Send, reply, and react to messages
- Error Handling - Catch errors and provide feedback
Prerequisites
Before starting, make sure you have:
- Bun installed (or Node.js 20+)
- A Hazel bot token
- Basic TypeScript knowledge
Tutorial Structure
Each step builds on the previous one. We recommend following them in order:
1. Project Setup
Initialize your bot project
2. Handling Messages
React to new messages
3. Slash Commands
Add typed slash commands
4. Message Operations
Send, reply, and react
5. Error Handling
Handle errors gracefully
Final Code Preview
Here's what the completed Task Bot looks like:
import { Effect, Schema } from "effect"
import { Command, CommandGroup, runHazelBot } from "@hazel/bot-sdk"
// In-memory task storage (use a database in production!)
const tasks: string[] = []
// Define commands
const AddTaskCommand = Command.make("task-add", {
description: "Add a new task",
args: { title: Schema.String },
usageExample: "/task-add Buy groceries",
})
const ListTasksCommand = Command.make("task-list", {
description: "List all tasks",
})
const commands = CommandGroup.make(AddTaskCommand, ListTasksCommand)
runHazelBot({
commands,
setup: (bot) =>
Effect.gen(function* () {
// Handle /task-add command
yield* bot.onCommand(AddTaskCommand, (ctx) =>
Effect.gen(function* () {
tasks.push(ctx.args.title)
yield* bot.message.send(ctx.channelId, `Added task: ${ctx.args.title}`)
}).pipe(bot.withErrorHandler(ctx)),
)
// Handle /task-list command
yield* bot.onCommand(ListTasksCommand, (ctx) =>
Effect.gen(function* () {
if (tasks.length === 0) {
yield* bot.message.send(ctx.channelId, "No tasks yet!")
return
}
const list = tasks.map((t, i) => `${i + 1}. ${t}`).join("\n")
yield* bot.message.send(ctx.channelId, `**Tasks:**\n${list}`)
}).pipe(bot.withErrorHandler(ctx)),
)
// React to messages mentioning "task"
yield* bot.onMessage((message) =>
Effect.gen(function* () {
if (message.content.toLowerCase().includes("task")) {
yield* bot.message.react(message, "📝")
}
}),
)
}),
})Ready to start? Let's begin with Project Setup!