Skip to main content
The Thread Summarizer creates a structured Markdown document from all messages in the current thread and delivers it as a .md file attachment. This is useful for archiving discussions, sharing context outside Discord, or feeding thread content into other tools.

Usage

Run /summarize inside any public thread. The bot responds immediately with an ephemeral acknowledgement and then uploads the file once it finishes processing.
/summarize small:true
The small option (default: true) wraps each message header in <small> tags to reduce visual size when the Markdown is rendered.
The /summarize command only works inside public threads. Running it in a regular channel or a private thread returns an ephemeral error.

Output format

The generated file follows this structure:
# Thread name

Thread started in: #channel-name<br />
Thread started at: Mon, 25 Mar 2026 09:00:00 GMT

<small>1: **username** &mdash; Mon, 25 Mar 2026 09:00:00 GMT</small><br />
First message content here.

<small>2: **other-user** (replying to #1) &mdash; Mon, 25 Mar 2026 09:01:00 GMT</small><br />
Reply content here.

![Attachment](https://cdn.discordapp.com/attachments/...)

Message format details

The bot uses the member’s server nickname (nick) when available, falling back to their Discord username. This means the summary reflects how members are known in your server.
When a message is a reply to an earlier message in the same thread, its header includes (replying to #N) where N is the 1-based index of the original message in the summary.
Image attachments are appended below the message content as standard Markdown image syntax:
![Attachment](https://cdn.discordapp.com/attachments/...)
When small is true (the default), message headers are wrapped in <small>...</small> tags:
<small>1: **username** <small>&mdash; Mon, 25 Mar 2026 09:00:00 GMT</small></small>
Set small:false to omit the tags if you are rendering the Markdown in a context that does not support HTML.

Summarize implementation

The core summarize function builds the document header and then processes each message concurrently:
Summarizer.ts
const summarize = (
  channel: Discord.GetChannel200,
  thread: Discord.ThreadResponse,
  messages: Array<Discord.MessageResponse>,
  small: boolean,
) => {
  const channelName = "name" in channel ? channel.name : "unknown-channel"
  return Effect.forEach(
    messages,
    (message, index) => {
      const reply = pipe(
        Option.fromNullishOr(message.message_reference),
        Option.flatMap((ref) => {
          const foundIndex = messages.findIndex(
            (_) => _.id === ref.message_id,
          )
          return foundIndex >= 0
            ? Option.some([messages[foundIndex], foundIndex + 1] as const)
            : Option.none()
        }),
      )
      return summarizeMessage(thread, index + 1, message, reply, small)
    },
    { concurrency: "unbounded" },
  ).pipe(
    Effect.map(
      (messageContent) =>
        `# ${thread.name}\n\nThread started in: #${channelName}<br />\nThread started at: ${new Date(
          thread.thread_metadata!.create_timestamp!,
        ).toUTCString()}\n\n${messageContent.join("\n\n")}`,
    ),
  )
}

Build docs developers (and LLMs) love