From 440cb741a8dc0d9e3dfca3df09fa5293da89d006 Mon Sep 17 00:00:00 2001 From: Nimesh Nayaju Date: Thu, 2 Apr 2026 08:37:19 -0400 Subject: [PATCH] Update README for Liveblocks Chat SDK Adapter (#3299) --- .../liveblocks-chat-sdk-adapter/README.md | 253 +++++++++++++++--- .../src/adapter.ts | 30 +++ 2 files changed, 245 insertions(+), 38 deletions(-) diff --git a/packages/liveblocks-chat-sdk-adapter/README.md b/packages/liveblocks-chat-sdk-adapter/README.md index 44c2264c590..bdfd1c95e84 100644 --- a/packages/liveblocks-chat-sdk-adapter/README.md +++ b/packages/liveblocks-chat-sdk-adapter/README.md @@ -1,57 +1,234 @@ -

- Liveblocks - Liveblocks -

- # `@liveblocks/chat-sdk-adapter` -

- NPM - Size - License -

- -`@liveblocks/chat-sdk-adapter` is a [Chat SDK](https://chat-sdk.dev) platform -adapter backed by [Liveblocks](https://liveblocks.io) **Comments**. It maps -rooms, threads, and comments to the Chat SDK’s `Channel` / `Thread` / `Message` -model so you can build bots that read and post in Liveblocks comment threads. +[Chat SDK](https://chat-sdk.dev/docs) adapter backed by +[Liveblocks Comments](https://liveblocks.io/docs/products/comments). It maps +Liveblocks rooms, threads, and comments to the Chat SDK’s `Channel` / `Thread` / +`Message` model so you can build bots that read and post in comment threads. ## Installation -``` +```bash npm install @liveblocks/chat-sdk-adapter chat ``` -## Documentation +See the [Chat SDK documentation](https://chat-sdk.dev/docs) for core concepts +and the +[Liveblocks API reference](https://liveblocks.io/docs/api-reference/liveblocks-chat-sdk-adapter) +for product-specific detail. + +## Usage + +Create the adapter, then pass it as `adapters.liveblocks` when constructing the +Chat SDK `Chat` instance. For a runnable bot (state, handlers, webhooks), see +[Full example](#full-example). + +```typescript +import { createLiveblocksAdapter } from "@liveblocks/chat-sdk-adapter"; -Read the -[documentation](https://liveblocks.io/docs/api-reference/liveblocks-chat-sdk-adapter) -for guides and API references. +const adapter = createLiveblocksAdapter({ + apiKey: process.env.LIVEBLOCKS_SECRET_KEY!, + webhookSecret: process.env.LIVEBLOCKS_WEBHOOK_SECRET!, + botUserId: "my-bot-user", + botUserName: "MyBot", +}); +``` + +## Configuration + +| Option | Type | Default | Description | +| ------------------- | ---------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------- | +| `apiKey` | `string` | — | Liveblocks secret key (`sk_...`) for REST API calls | +| `webhookSecret` | `string` | — | Webhook signing secret (`whsec_...`) from the dashboard | +| `botUserId` | `string` | — | User ID used when the bot creates, edits, or reacts to comments; must match your app’s user identifiers | +| `botUserName` | `string` | `"liveblocks-bot"` | Display name for the bot | +| `resolveUsers` | `function` | — | Resolves user IDs for @mentions; return one entry per input id in order, or `undefined` to skip (see TSDoc types) | +| `resolveGroupsInfo` | `function` | — | Resolves group IDs for @mentions; same ordering rules as `resolveUsers` | +| `logger` | `Logger` | `ConsoleLogger("info")` child | Chat SDK–compatible logger | + +Resolver return types follow `@liveblocks/core` user and group metadata shapes +(`U["info"]`, `DGI`). + +### Resolving mentions + +When comments contain @mentions, provide `resolveUsers` and optional +`resolveGroupsInfo`: + +```typescript +const adapter = createLiveblocksAdapter({ + apiKey: process.env.LIVEBLOCKS_SECRET_KEY!, + webhookSecret: process.env.LIVEBLOCKS_WEBHOOK_SECRET!, + botUserId: "my-bot-user", + + resolveUsers: async ({ userIds }) => { + const users = await getUsersFromDatabase(userIds); + return users.map((user) => ({ + name: user.fullName, + avatar: user.avatarUrl, + })); + }, + + resolveGroupsInfo: async ({ groupIds }) => { + const groups = await getGroupsFromDatabase(groupIds); + return groups.map((group) => ({ name: group.displayName })); + }, +}); +``` -## Examples +## Platform setup + +1. Create a [Liveblocks project](https://liveblocks.io/docs/get-started) with + rooms using [Comments](https://liveblocks.io/docs/products/comments). +2. In the dashboard, copy a **secret key** (`sk_...`) for server-side REST API + calls. +3. Create a **webhook signing secret** (`whsec_...`) and configure webhooks to + subscribe to: + - `commentCreated` + - `commentReactionAdded` + - `commentReactionRemoved` +4. Choose a stable `botUserId` consistent with how your app identifies users + (the bot should be a real user ID in your system or a dedicated bot ID you + issue). + +Point your Liveblocks webhook URL at the route that forwards requests to +`bot.webhooks.liveblocks` (see [Webhook events](#webhook-events)). + +## Webhook events + +Supported Liveblocks webhook types: + +| Event | Role | +| ------------------------ | ---------------------------------- | +| `commentCreated` | Drives Chat SDK message processing | +| `commentReactionAdded` | Drives reaction handlers | +| `commentReactionRemoved` | Drives reaction handlers | + +```typescript +export async function POST(request: Request) { + return bot.webhooks.liveblocks(request, { + waitUntil: (p) => void p, + }); +} +``` -Explore our [collaborative examples](https://liveblocks.io/examples) to help you -get started. +The adapter verifies signatures with `webhookSecret`; invalid requests get +**401**. -> All examples are open-source and live in this repository, within -> [`/examples`](../../examples). +> **Serverless:** Passing `waitUntil` (e.g. on Vercel) lets work continue after +> the response is sent. -## Releases +## ID encoding -See the [latest changes](https://github.com/liveblocks/liveblocks/releases) or -learn more about -[upcoming releases](https://github.com/liveblocks/liveblocks/milestones). +- **Thread ID:** `liveblocks:{roomId}:{threadId}` +- **Channel ID:** `liveblocks:{roomId}` -## Community +### `encodeThreadId` -- [Discord](https://liveblocks.io/discord) - To get involved with the Liveblocks - community, ask questions and share tips. -- [X](https://x.com/liveblocks) - To receive updates, announcements, blog posts, - and general Liveblocks tips. +```typescript +adapter.encodeThreadId(data: { roomId: string; threadId: string }): string; +``` -## License +```typescript +const encoded = adapter.encodeThreadId({ + roomId: "my-room", + threadId: "th_abc123", +}); +// "liveblocks:my-room:th_abc123" +``` + +### `decodeThreadId` + +```typescript +adapter.decodeThreadId(threadId: string): { roomId: string; threadId: string }; +``` + +Throws if the format is invalid. Room IDs may contain `:`; the **last** `:` +separates `threadId`, so Liveblocks thread IDs must not contain `:`. + +## Features + +| Area | Support | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Threads / channels | Maps rooms and comment threads to Chat SDK models | +| Post, edit, delete | Comments via REST | +| Reactions | Unicode emoji only; names like `thumbs_up` normalize to emoji where supported; unknown custom ids can fail API validation — e.g. `addReaction(…, "👍")` or `"thumbs_up"` | +| Mentions | Users and groups when resolvers are provided | +| Attachments | Fetched via Liveblocks attachment URLs | +| Typing indicators | Not supported — `startTyping` is a no-op | + +### Message format + +Liveblocks Comments use a simpler body model than full Markdown. Outbound +content from the Chat SDK is converted automatically; some structure is +flattened. + +**Supported:** paragraphs with bold, italic, code, strikethrough, links, +@mentions (users and groups). + +**Flattened to plain text / paragraphs:** headings, bullet and numbered lists, +code blocks, tables (ASCII in a paragraph), raw HTML. Card payloads become +markdown/plain text (or `fallbackText`); interactivity is not preserved. + +## Full example + +```typescript +import { Chat } from "chat"; +import { + createLiveblocksAdapter, + type LiveblocksAdapter, +} from "@liveblocks/chat-sdk-adapter"; +import { createMemoryState } from "@chat-adapter/state-memory"; + +const bot = new Chat<{ liveblocks: LiveblocksAdapter }>({ + userName: "MyBot", + adapters: { + liveblocks: createLiveblocksAdapter({ + apiKey: process.env.LIVEBLOCKS_SECRET_KEY!, + webhookSecret: process.env.LIVEBLOCKS_WEBHOOK_SECRET!, + botUserId: "my-bot-user", + botUserName: "MyBot", + resolveUsers: async ({ userIds }) => { + const users = await getUsersFromDatabase(userIds); + return users.map((user) => ({ name: user.fullName })); + }, + }), + }, + state: createMemoryState(), +}); + +bot.onNewMention(async (thread, message) => { + await thread.adapter.addReaction(thread.id, message.id, "👀"); + await thread.post(`Hello, ${message.author.userName}!`); +}); + +bot.onReaction(async (event) => { + if (!event.added) return; + await event.adapter.postMessage( + event.threadId, + `${event.user.userName} reacted with "${event.emoji.name}"` + ); +}); +``` -Licensed under the Apache License 2.0, Copyright © 2021-present -[Liveblocks](https://liveblocks.io). +Wire Liveblocks to the same webhook handler as in +[Webhook events](#webhook-events) (for example a Next.js `POST` route that calls +`bot.webhooks.liveblocks`). + +## Examples using Chat SDK + +- **[Chat SDK Bot](https://liveblocks.io/examples/chat-sdk-bot/nextjs-chat-sdk-bot)** + — Next.js bot that responds to @mentions and reactions in Liveblocks comment + threads + ([source](https://github.com/liveblocks/liveblocks/tree/main/examples/nextjs-chat-sdk-bot)). +- **[Chat SDK AI Bot](https://liveblocks.io/examples/chat-sdk-ai-bot/nextjs-chat-sdk-ai-bot)** + — Same stack with an AI-powered reply flow + ([source](https://github.com/liveblocks/liveblocks/tree/main/examples/nextjs-chat-sdk-ai-bot)). + +Full walkthrough: +[Get started with a Chat SDK bot using Liveblocks and Next.js](https://liveblocks.io/docs/get-started/nextjs-chat-sdk-bot). + +More collaborative examples: +[liveblocks.io/examples](https://liveblocks.io/examples). + +## License -See [LICENSE](../../licenses/LICENSE-APACHE-2.0) for more information. +Apache License 2.0. See [LICENSE](../../licenses/LICENSE-APACHE-2.0). diff --git a/packages/liveblocks-chat-sdk-adapter/src/adapter.ts b/packages/liveblocks-chat-sdk-adapter/src/adapter.ts index ee55560edf4..b18360b603b 100644 --- a/packages/liveblocks-chat-sdk-adapter/src/adapter.ts +++ b/packages/liveblocks-chat-sdk-adapter/src/adapter.ts @@ -1316,19 +1316,49 @@ export interface LiveblocksAdapterConfig< U extends BaseUserMeta, DGI extends BaseGroupInfo, > { + /** + * The Liveblocks secret key. Must start with "sk_". Get it from the Liveblocks dashboard: https://liveblocks.io/dashboard/apikeys + */ apiKey: string; + /** + * The Liveblocks webhook signing secret. Get it from the Liveblocks dashboard: https://liveblocks.io/dashboard/webhooks + * @example "whsec_wPbvQ+u3VtN2e2tRPDKchQ1tBZ3svaHLm" + */ webhookSecret: string; + /** + * A function that returns user info from user IDs; used to resolve @user mentions in comment bodies. + * This function should return an array of user info in the same order as the input user IDs, or `undefined` to skip resolution. + */ resolveUsers?: ( args: ResolveUsersArgs ) => Awaitable<(U["info"] | undefined)[] | undefined>; + /** + * A function that returns group info from group IDs; used to resolve @group mentions in comment bodies. + * This function should return an array of group info in the same order as the input group IDs, or `undefined` to skip resolution. + */ resolveGroupsInfo?: ( args: ResolveGroupsInfoArgs ) => Awaitable<(DGI | undefined)[] | undefined>; + /** + * The user ID used when the bot creates, edits, or reacts to comments. + * This should match your app’s user identifiers. + */ botUserId: string; + /** + * The display name for the chat user representing the bot. + * @default "liveblocks-bot" + */ botUserName?: string; + /** + * A Chat SDK–compatible logger. + * @default ConsoleLogger at info level, scoped to this adapter + */ logger?: Logger; } +/** + * Creates a {@link LiveblocksAdapter} configured for Liveblocks Comments. + */ export function createLiveblocksAdapter< U extends BaseUserMeta = BaseUserMeta, DGI extends BaseGroupInfo = BaseGroupInfo,