Visitar URL original
Creating a Chat Instance | Chat SDK

Creating a Chat Instance

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

lib/bot.ts
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.");
});

This example uses Redis. Chat SDK also supports PostgreSQL and ioredis as production state adapters. See State Adapters for all options.

Adapter and state factories read their credentials from environment variables such as SLACK_BOT_TOKEN, SLACK_SIGNING_SECRET, and REDIS_URL, so the example above passes no options. Pass explicit values to override them. For setup UIs and build scripts, @chat-adapter/catalog lists official and vendor-official adapter env specs without importing adapter packages.

Multiple adapters

Register several adapters to run the same bot on each of those platforms:

lib/bot.ts
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 the same way for every registered adapter, because the SDK normalizes messages, threads, and reactions into one format. Where platforms differ, such as rate limits or unsupported features, the SDK throws typed errors. See Error Handling.

Configuration options

OptionTypeDefaultDescription
userNamestringrequiredDefault bot username across all adapters
adaptersRecord<string, Adapter>requiredMap of adapter name to adapter instance
stateStateAdapterrequiredState adapter for subscriptions and locking
loggerLogger | LogLevel"info"Logger instance or log level ("debug", "info", "warn", "error", "silent")
dedupeTtlMsnumber600000TTL in ms for message deduplication (10 minutes)
concurrency"drop" | "queue" | "debounce" | "burst" | "concurrent" | ConcurrencyConfig"drop"Strategy for overlapping messages on the same thread
streamingUpdateIntervalMsnumber500Update interval in ms for post+edit streaming
fallbackStreamingPlaceholderTextstring | null"..."Placeholder text while streaming starts. Set to null to skip
lockScope"thread" | "channel" | functionAdapter defaultWhether messages share a lock per thread or per channel. See Lock scope
historyHistoryConfigUser, thread, and channel history settings. See History
onLockConflict'drop' | 'force' | (threadId, message) => 'drop' | 'force'"drop"Deprecated: use concurrency. Under the drop strategy, 'force' releases the existing lock and re-acquires it so a new message can interrupt a long-running handler

Accessing adapters

Use getAdapter to access platform-specific APIs when you need functionality beyond the unified interface:

lib/bot.ts
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:

lib/bot.ts
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 for multi-tenant constraints.

Webhook routing

The webhooks property provides a typed handler for each registered adapter. Wire each one to a route in your HTTP framework:

app/api/webhooks/slack/route.ts
import { after } from "next/server";
import { bot } from "@/lib/bot";

export async function POST(request: Request): Promise<Response> {
  return bot.webhooks.slack(request, {
    waitUntil: (task) => after(() => task),
  });
}
app/api/webhooks/teams/route.ts
import { after } from "next/server";
import { bot } from "@/lib/bot";

export async function POST(request: Request): Promise<Response> {
  return bot.webhooks.teams(request, {
    waitUntil: (task) => after(() => task),
  });
}

Your event handlers can keep running after the webhook responds. waitUntil keeps the function alive until they finish, so a serverless platform doesn't stop them early. These examples use Next.js after. In other Vercel Functions, pass waitUntil from @vercel/functions.

Lifecycle

The Chat instance initializes lazily on the first webhook. You can also initialize manually:

lib/bot.ts
await bot.initialize();

For graceful shutdown, such as during serverless teardown, call shutdown:

lib/bot.ts
await bot.shutdown();

Singleton pattern

Register a singleton when you need to access the Chat instance from multiple files:

lib/bot.ts
const bot = new Chat({ /* ...config */ }).registerSingleton();
export default bot;
lib/utils.ts
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:

lib/bot.ts
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:

lib/bot.ts
const channel = bot.channel("slack:C123ABC");
await channel.post("Announcement: deploy complete!");

Read more