UNPKG

chat

Version:

Unified chat abstraction for Slack, Teams, Google Chat, and Discord

269 lines (240 loc) 7.71 kB
--- title: Message description: Normalized message format with text, AST, author, and metadata. type: reference --- Incoming messages are normalized across all platforms into a consistent `Message` object. ```typescript import { Message } from "chat"; ``` ## Properties <TypeTable type={{ id: { description: 'Platform-specific message ID.', type: 'string', }, threadId: { description: 'Thread ID in adapter:channel:thread format.', type: 'string', }, text: { description: 'Plain text content with all formatting stripped.', type: 'string', }, formatted: { description: 'mdast AST representation — the canonical format for processing.', type: 'Root', }, raw: { description: 'Original platform-specific payload (escape hatch).', type: 'unknown', }, author: { description: 'Message author info.', type: 'Author', }, metadata: { description: 'Timestamps and edit status.', type: 'MessageMetadata', }, attachments: { description: 'File attachments.', type: 'Attachment[]', }, links: { description: 'Links found in the message, with optional preview metadata.', type: 'LinkPreview[]', }, isMention: { description: 'Whether the bot was @-mentioned in this message.', type: 'boolean | undefined', }, subject: { description: 'Resolves the parent resource (issue, PR) this message is about. Returns null on chat platforms. See [Message Subject](/docs/subject).', type: 'Promise<MessageSubject | null>', }, }} /> ## Author <TypeTable type={{ userId: { description: 'Platform-specific user ID.', type: 'string', }, userName: { description: 'Username/handle for @-mentions.', type: 'string', }, fullName: { description: 'Display name.', type: 'string', }, isBot: { description: 'Whether the author is a bot.', type: 'boolean | "unknown"', }, isMe: { description: 'Whether the author is this bot.', type: 'boolean', }, }} /> ### How `isMe` works Each adapter detects whether a message came from the bot itself. The detection logic varies by platform: | Platform | Detection method | |----------|-----------------| | Slack | Checks `event.user === botUserId` (primary), then `event.bot_id === botId` (for `bot_message` subtypes). Both IDs are fetched during initialization via `auth.test`. | | Teams | Checks `activity.from.id === appId` (exact match), then checks if `activity.from.id` ends with `:{appId}` (handles `28:{appId}` format). | | Google Chat | Checks `message.sender.name === botUserId`. The bot user ID is learned dynamically from message annotations when the bot is first @-mentioned. | <Callout type="info"> All adapters return `false` if the bot ID isn't known yet. This is a safe default that prevents the bot from ignoring messages it should process. </Callout> ## MessageMetadata <TypeTable type={{ dateSent: { description: 'When the message was sent.', type: 'Date', }, edited: { description: 'Whether the message has been edited.', type: 'boolean', }, editedAt: { description: 'When the message was last edited.', type: 'Date | undefined', }, }} /> ## Attachment <TypeTable type={{ type: { description: 'Attachment type.', type: '"image" | "file" | "video" | "audio"', }, url: { description: 'URL to the file.', type: 'string | undefined', }, data: { description: 'Binary data (if already fetched).', type: 'Buffer | Blob | undefined', }, name: { description: 'Filename.', type: 'string | undefined', }, mimeType: { description: 'MIME type.', type: 'string | undefined', }, size: { description: 'File size in bytes.', type: 'number | undefined', }, 'fetchData()': { description: 'Fetch the attachment data. Handles platform auth automatically.', type: '() => Promise<Buffer> | undefined', }, fetchMetadata: { description: 'Platform-specific IDs for reconstructing fetchData after serialization (e.g. WhatsApp mediaId, Telegram fileId).', type: 'Record<string, string> | undefined', }, }} /> ## LinkPreview Links found in incoming messages are extracted and exposed as `LinkPreview` objects. On platforms that support it (currently Slack), links pointing to other chat messages include a `fetchMessage()` callback to retrieve the full linked message. <TypeTable type={{ url: { description: 'The URL.', type: 'string', }, title: { description: 'Title from unfurl metadata (if available).', type: 'string | undefined', }, description: { description: 'Description from unfurl metadata (if available).', type: 'string | undefined', }, imageUrl: { description: 'Preview image URL (if available).', type: 'string | undefined', }, siteName: { description: 'Site name, e.g. "Vercel" (if available).', type: 'string | undefined', }, 'fetchMessage()': { description: 'Fetch the linked chat message. Available when the URL points to a message on the same platform (e.g. a Slack message link).', type: '() => Promise<Message> | undefined', }, }} /> <Callout type="info"> When using [`toAiMessages()`](/docs/ai/to-ai-messages), link metadata is automatically appended to the message content. Embedded message links are labeled as `[Embedded message: ...]` so the AI model understands the context. </Callout> ### Platform support | Platform | Link extraction | `fetchMessage()` | |----------|----------------|-------------------| | Slack | URLs from `rich_text` blocks or `<url>` text patterns | Slack message links (`*.slack.com/archives/...`) | | Others | Not yet `links` is always `[]` | | ## MessageSubject Returned by `message.subject` on platforms with parent resources. See [Message Subject](/docs/subject) for usage. <TypeTable type={{ type: { description: 'Resource kind, e.g. "issue" or "pull_request".', type: 'string', }, id: { description: 'Resource identifier (e.g. "ENG-123" or "42").', type: 'string', }, title: { description: 'Resource title.', type: 'string | undefined', }, description: { description: 'Full description/body in markdown.', type: 'string | undefined', }, status: { description: 'Current status (e.g. "In Progress", "open").', type: 'string | undefined', }, url: { description: 'Web URL to the resource.', type: 'string | undefined', }, author: { description: 'Resource creator.', type: '{ id: string; name: string } | undefined', }, assignee: { description: 'Current assignee.', type: '{ id: string; name: string } | undefined', }, labels: { description: 'Labels/tags.', type: 'string[] | undefined', }, raw: { description: 'Full platform API response.', type: 'unknown', }, }} /> ## Serialization Messages can be serialized for workflow engines and external systems. ```typescript // Serialize const json = message.toJSON(); // Deserialize const restored = Message.fromJSON(json); ``` The serialized format converts `Date` fields to ISO strings and omits non-serializable fields like `data` buffers and `fetchData` functions. The `fetchMetadata` field is preserved so that adapters can reconstruct `fetchData` when the message is rehydrated from a queue.