How-to Guides
Multi-Platform Adapter Pattern
Adapter interface and Bridges you wire. Not a finished 14-channel helpdesk.
Multi-Platform Adapter Pattern
The Worker ships an Adapter<TRawMessage> interface and format converters. Console Bridges are paste-token rows: you create the Slack, Discord, Telegram, WhatsApp, or Teams app and point the webhook at the Worker. This is not a finished 14-channel helpdesk.
Overview
The adapter pattern abstracts platform-specific concerns behind a common interface so you can add a channel without rewriting the room kernel.
Interfaces in this repo (not a desk SKU): Web, Slack, Microsoft Teams, Discord, Telegram, WhatsApp, Google Chat, GitHub, Linear, Matrix, Resend (Email), IRC, Twitch, Line, and a generic API adapter. Gold path today is Bridges for Slack/Discord/Telegram/WhatsApp/Teams that you wire yourself.
Adapter Interface
Every adapter implements:
interface Adapter<TRawMessage> {
handleWebhook(raw: Request): Promise<TRawMessage>;
parseMessage(raw: TRawMessage): ParsedMessage;
postMessage(threadId: string, content: string): Promise<void>;
editMessage(threadId: string, messageId: string, content: string): Promise<void>;
deleteMessage(threadId: string, messageId: string): Promise<void>;
stream(threadId: string, content: ReadableStream): Promise<void>;
encodeThreadId(parts: ThreadIdParts): string;
decodeThreadId(threadId: string): ThreadIdParts;
formatConverter: FormatConverter;
lockScope: "thread" | "channel";
}Format Converter
Each adapter includes a FormatConverter that translates between canonical mdast (Markdown Abstract Syntax Tree) and the platform's native format:
- Slack: mrkdwn with Block Kit
- Teams: Adaptive Cards
- Discord: Markdown with embeds
- Telegram: MarkdownV2 with inline keyboards
- WhatsApp: Plain text with formatting shortcuts
- Web: Full markdown rendering
import { getAdapter } from "@fluxy-chat/sdk";
const slack = getAdapter("slack");
const threadId = slack.encodeThreadId({ channelId: "C123", ts: "1234" });
// "slack\:C123:1234"
const parsed = slack.parseMessage(rawWebhookBody);
await slack.postMessage(threadId, "Hello from FluxyChat!");WebAdapter
The WebAdapter wraps FluxyChat's native WebSocket + REST flow into the adapter interface. It's the default adapter and is registered at worker startup.
import { getAdapter } from "@fluxy-chat/sdk";
const web = getAdapter("web");
const threadId = web.encodeThreadId({ userId: "alice", roomId: "general" });
// "web:alice\:general"Adding a Custom Adapter
import { BaseAdapter } from "@fluxy-chat/sdk";
class MyAdapter extends BaseAdapter {
slug = "myapp" as const;
async handleWebhook(req: Request) { /* ... */ }
parseMessage(raw: MyRawMessage) { /* ... */ }
async postMessage(threadId: string, content: string) { /* ... */ }
// ... implement remaining methods
}Adapter Catalog
The static adapter registry (adapters-http.js) provides metadata for discovery and onboarding UIs:
import { listAdapters } from "@fluxy-chat/sdk";
const adapters = listAdapters();
// [{ slug: "web", name: "Web", packageName: "@fluxy-chat/adapter-web" }, ...]Lock Scope
Adapters declare whether they lock at the thread level (Slack, Discord) or channel level (WhatsApp, Telegram). This prevents concurrent message processing conflicts per platform's threading model.
See Also
- Streaming Markdown Guide — How markdown renders during streaming
- Card Builder Guide — Rich message elements across platforms