FluxyChat

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

On this page