chat
Version:
Unified chat abstraction for Slack, Teams, Google Chat, and Discord
167 lines (122 loc) • 5.89 kB
text/mdx
---
title: Creating a Chat Instance
description: Initialize the Chat class with adapters, state, and configuration options.
type: guide
prerequisites:
- /docs/getting-started
related:
- /docs/handling-events
- /docs/adapters
- /docs/state
---
The `Chat` class is the main entry point for your bot. It coordinates adapters, routes events to your handlers, and manages thread state.
## Basic setup
```typescript title="lib/bot.ts" lineNumbers
import { Chat } from "chat";
import { createSlackAdapter } from "@chat-adapter/slack";
import { createRedisState } from "@chat-adapter/state-redis";
const bot = new Chat({
userName: "mybot",
adapters: {
slack: createSlackAdapter(),
},
state: createRedisState(),
});
bot.onNewMention(async (thread) => {
await thread.subscribe();
await thread.post("Hello! I'm listening to this thread.");
});
```
<Callout type="info">
This example uses Redis. Chat SDK also supports [PostgreSQL](/adapters/official/postgres) and [ioredis](/adapters/official/ioredis) as production state adapters. See [State Adapters](/docs/state) for all options.
</Callout>
Each adapter factory auto-detects credentials from environment variables (`SLACK_BOT_TOKEN`, `SLACK_SIGNING_SECRET`, `REDIS_URL`, etc.), so you can get started with zero config. Pass explicit values to override.
## Multiple adapters
Register multiple [adapters](/adapters) to deploy your bot across platforms simultaneously:
```typescript title="lib/bot.ts" lineNumbers
import { Chat } from "chat";
import { createSlackAdapter } from "@chat-adapter/slack";
import { createTeamsAdapter } from "@chat-adapter/teams";
import { createDiscordAdapter } from "@chat-adapter/discord";
import { createRedisState } from "@chat-adapter/state-redis";
const bot = new Chat({
userName: "mybot",
adapters: {
slack: createSlackAdapter(),
teams: createTeamsAdapter(),
discord: createDiscordAdapter(),
},
state: createRedisState(),
});
```
Your event handlers work identically across all registered adapters — the SDK normalizes messages, threads, and reactions into a consistent format.
## Configuration options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `userName` | `string` | *required* | Default bot username across all adapters |
| `adapters` | `Record<string, Adapter>` | *required* | Map of adapter name to adapter instance |
| `state` | `StateAdapter` | *required* | State adapter for subscriptions and locking |
| `logger` | `Logger \| LogLevel` | `"info"` | Logger instance or log level (`"debug"`, `"info"`, `"warn"`, `"error"`, `"silent"`) |
| `dedupeTtlMs` | `number` | `300000` | TTL in ms for message deduplication (5 minutes) |
| `concurrency` | `"drop" \| "queue" \| "debounce" \| "burst" \| "concurrent" \| ConcurrencyConfig` | `"drop"` | Strategy for overlapping messages on the same thread |
| `streamingUpdateIntervalMs` | `number` | `500` | Update interval in ms for post+edit streaming |
| `fallbackStreamingPlaceholderText` | `string \| null` | `"..."` | Placeholder text while streaming starts. Set to `null` to skip |
| `onLockConflict` | `'drop' \| 'force' \| (threadId, message) => 'drop' \| 'force'` | `"drop"` | Behavior when a thread lock is already held. `'force'` releases the existing lock and re-acquires it, enabling interrupt/steerability for long-running handlers |
## Accessing adapters
Use `getAdapter` to access platform-specific APIs when you need functionality beyond the unified interface:
```typescript title="lib/bot.ts" lineNumbers
import type { SlackAdapter } from "@chat-adapter/slack";
const slack = bot.getAdapter("slack") as SlackAdapter;
await slack.setSuggestedPrompts(channelId, threadTs, [
{ title: "Get started", message: "What can you help me with?" },
]);
```
For typed access to the platform's native API client, use the SDK-named getter on each adapter:
```typescript title="lib/bot.ts" lineNumbers
const slack = bot.getAdapter("slack").webClient; // WebClient
const linear = bot.getAdapter("linear").linearClient; // LinearClient
const github = bot.getAdapter("github").octokit; // Octokit
```
The previous `.client` getter still works as a deprecated alias on all three adapters.
See [`getAdapter`](/docs/api/chat#getadapter) for multi-tenant constraints.
## Webhook routing
The `webhooks` property provides type-safe handlers for each registered adapter. Wire these up to your HTTP framework's routes:
```typescript title="app/api/webhooks/slack/route.ts" lineNumbers
import { bot } from "@/lib/bot";
export const POST = bot.webhooks.slack;
```
```typescript title="app/api/webhooks/teams/route.ts" lineNumbers
import { bot } from "@/lib/bot";
export const POST = bot.webhooks.teams;
```
## Lifecycle
The Chat instance initializes lazily on the first webhook. You can also initialize manually:
```typescript title="lib/bot.ts" lineNumbers
await bot.initialize();
```
For graceful shutdown (e.g. in serverless teardown), call `shutdown`:
```typescript title="lib/bot.ts" lineNumbers
await bot.shutdown();
```
## Singleton pattern
Register a singleton when you need to access the Chat instance from multiple files:
```typescript title="lib/bot.ts" lineNumbers
const bot = new Chat({ /* ...config */ }).registerSingleton();
export default bot;
```
```typescript title="lib/utils.ts" lineNumbers
import { Chat } from "chat";
const bot = Chat.getSingleton();
```
## Direct messaging
Open a DM thread with a user by passing their platform user ID or an `Author` object:
```typescript title="lib/bot.ts" lineNumbers
const dm = await bot.openDM("U123ABC");
await dm.post("Hey! Just wanted to follow up on your request.");
```
## Channel access
Get a channel directly by its ID:
```typescript title="lib/bot.ts" lineNumbers
const channel = bot.channel("slack:C123ABC");
await channel.post("Announcement: deploy complete!");
```