Skip to main content
The Message class represents a chat message with structured formatting (mdast AST), metadata, attachments, and platform-specific raw data. It supports serialization for workflow engines.

Constructor

new Message<TRawMessage>(data: MessageData<TRawMessage>)
data
MessageData<TRawMessage>
required
Message data object
message
Message<TRawMessage>
Message instance

Properties

id

readonly id: string
Unique message ID.

threadId

readonly threadId: string
Thread this message belongs to.

text

text: string
Plain text content with all formatting stripped.
console.log(message.text); // "Hello world"

formatted

formatted: FormattedContent
Structured formatting as an mdast AST (Root). This is the canonical representation - use this for processing.
Use stringifyMarkdown(message.formatted) from the chat package to convert the AST back to a markdown string.
import { stringifyMarkdown } from "chat";

const markdown = stringifyMarkdown(message.formatted);
console.log(markdown); // "**Hello** _world_"

raw

raw: TRawMessage
Platform-specific raw payload. Use this as an escape hatch when you need to access platform-specific fields not exposed by the normalized Message interface.
// Slack-specific data
const slackMessage = message.raw as SlackMessage;
if (slackMessage.team_id) {
  console.log(`Team: ${slackMessage.team_id}`);
}

author

author: Author
Message author information.
author
Author
console.log(`From: ${message.author.fullName}`);
if (message.author.isBot) {
  console.log("This is a bot message");
}

metadata

metadata: MessageMetadata
Message metadata including timestamps and edit status.
metadata
MessageMetadata
console.log(`Sent: ${message.metadata.dateSent.toISOString()}`);
if (message.metadata.edited) {
  console.log(`Edited: ${message.metadata.editedAt?.toISOString()}`);
}

attachments

attachments: Attachment[]
Files, images, videos, or audio attached to the message.
attachment
Attachment
for (const attachment of message.attachments) {
  if (attachment.type === "image") {
    console.log(`Image: ${attachment.url}`);
    console.log(`Size: ${attachment.width}x${attachment.height}`);
  }
}

isMention

isMention?: boolean
Whether the bot is @-mentioned in this message.
This is set by the Chat SDK before passing the message to handlers. It checks for @username in the message text using the adapter’s configured userName and optional botUserId.
chat.onSubscribedMessage(async (thread, message) => {
  if (message.isMention) {
    await thread.post("You mentioned me!");
  }
});

Methods

toJSON()

toJSON(): SerializedMessage
Serialize the message to a plain JSON object. Use this to pass message data to external systems like workflow engines.
serialized
SerializedMessage
Serialized message with dates as ISO strings
Attachment data (Buffer) and fetchData (function) are omitted as they’re not serializable.
const serialized = message.toJSON();
await workflow.start("process-message", { message: serialized });

Static Methods

fromJSON()

static fromJSON<TRawMessage = unknown>(
  json: SerializedMessage
): Message<TRawMessage>
Reconstruct a Message from serialized JSON data. Converts ISO date strings back to Date objects.
json
SerializedMessage
required
Serialized message data
message
Message<TRawMessage>
Reconstructed Message instance
const message = Message.fromJSON(serializedMessage);
console.log(message.metadata.dateSent); // Date object

Type Parameters

TRawMessage
unknown
default:"unknown"
Platform-specific raw message type

Usage Examples

Accessing Message Content

chat.onNewMention(async (thread, message) => {
  console.log(`User: ${message.author.fullName}`);
  console.log(`Text: ${message.text}`);
  console.log(`Sent: ${message.metadata.dateSent.toISOString()}`);
  
  if (message.attachments.length > 0) {
    console.log(`Attachments: ${message.attachments.length}`);
  }
});

Checking for Mentions

chat.onSubscribedMessage(async (thread, message) => {
  if (message.isMention) {
    await thread.post(`Thanks for mentioning me, ${message.author.fullName}!`);
  }
});

Processing Attachments

chat.onNewMessage(/^analyze image/, async (thread, message) => {
  const images = message.attachments.filter(a => a.type === "image");
  
  if (images.length === 0) {
    await thread.post("Please attach an image to analyze.");
    return;
  }
  
  for (const image of images) {
    console.log(`Processing: ${image.name}`);
    console.log(`Size: ${image.width}x${image.height}`);
    
    // Fetch image data if needed
    if (image.fetchData) {
      const data = await image.fetchData();
      // Process image data...
    }
  }
});

Working with Formatted Content

import { stringifyMarkdown, toPlainText } from "chat";

chat.onSubscribedMessage(async (thread, message) => {
  // Get as markdown
  const markdown = stringifyMarkdown(message.formatted);
  console.log(`Markdown: ${markdown}`);
  
  // Get as plain text (same as message.text)
  const plain = toPlainText(message.formatted);
  console.log(`Plain: ${plain}`);
});

Serialization for Workflows

import { Chat } from "chat";
import { workflow } from "@workflow/engine";

chat.onNewMention(async (thread, message) => {
  // Serialize message for workflow
  const serialized = message.toJSON();
  
  await workflow.start("process-inquiry", {
    message: serialized,
    thread: thread.toJSON(),
  });
});

// In workflow handler
workflow.on("process-inquiry", async (data) => {
  // Deserialize message
  const message = Message.fromJSON(data.message);
  console.log(message.text); // Works!
  console.log(message.metadata.dateSent); // Date object restored
});

Platform-Specific Data

interface SlackMessage {
  team_id?: string;
  channel?: string;
  ts?: string;
  blocks?: unknown[];
}

chat.onNewMention(async (thread, message) => {
  // Access Slack-specific fields
  const slackData = message.raw as SlackMessage;
  
  if (slackData.team_id) {
    console.log(`Team: ${slackData.team_id}`);
  }
  
  if (slackData.blocks) {
    console.log(`Has ${slackData.blocks.length} Block Kit blocks`);
  }
});

See Also