Slack: bulleted lists and special mentions are lost
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 2.4k
- Forks
- 314
- Avg merge
- 1d 18h
- Merged PRs (30d)
- 63
Description
Bug Description
For an inbound message the adapter builds message.formatted from event.text alone (this.formatConverter.toAst(text) in parseSlackMessage). For messages typed in Slack's composer, event.text is a lossy rendering of the message, and the structure is in event.blocks as a rich_text block. The adapter reads event.blocks to collect link URLs (extractLinks) and, in 4.40.0, to build tables from table / data_table blocks, but not for the message body. Two kinds of content are lost:
- Lists. A bulleted list made with the composer arrives in
event.textas lines starting with•, with◦and four spaces for a nested item. Neither is markdown list syntax, so the result is oneparagraphof literal bullet characters. There is nolistnode, and the nesting indent is dropped onstringifyMarkdown.event.blockscarries the same list asrich_text_listelements withstyleandindent. - Special mentions.
<!here>,<!channel>,<!everyone>, and<!subteam^S…|@handle>are not converted byslackMrkdwnToMarkdown. A message containing one parses to an mdasthtmlnode, and the raw token appears in bothmessage.textand the markdown.event.blockscarries them asbroadcastandusergroupelements.
Steps to Reproduce
- In Slack, use the composer's bulleted-list button to send the bot a list with one nested item, followed by a line containing
@hereand a user-group mention. - Read
message.formatted,message.text, andstringifyMarkdown(message.formatted)in the handler, or run the code sample below, which passes an event of that shape toadapter.parseMessage.
Expected Behavior
message.formattedcontains alistnode with twolistItemchildren, the second holding a nestedlist.stringifyMarkdowngives* one\n* two\n * nested.- Special mentions become readable text such as
@hereand@devs, consistent with how<@U…|name>becomes@name, and the message does not parse to anhtmlnode.
Actual Behavior
Output of the code sample on 4.40.0:
nodes : ["paragraph","html"]
text : "Steps:\n• one\n• two\n◦ nested\n\n<!here> and <!subteam^S0123456789|@devs>"
markdown: "Steps:\n• one\n• two\n◦ nested\n\n<!here> and <!subteam^S0123456789|@devs>\n"
4.30.0 gives the same nodes and markdown.
Code Sample
import { createSlackAdapter } from "@chat-adapter/slack";
import { stringifyMarkdown } from "chat";
const adapter = createSlackAdapter({ botToken: "xoxb-test", signingSecret: "test" });
const section = (text: string) => ({ type: "rich_text_section", elements: [{ type: "text", text }] });
const message = adapter.parseMessage({
type: "message",
channel: "D0123456789",
channel_type: "im",
user: "U0123456789",
username: "jane",
ts: "1726600000.000100",
text: "Steps:\n• one\n• two\n ◦ nested\n<!here> and <!subteam^S0123456789|@devs>",
blocks: [
{
type: "rich_text",
elements: [
section("Steps:"),
{ type: "rich_text_list", style: "bullet", indent: 0, elements: [section("one"), section("two")] },
{ type: "rich_text_list", style: "bullet", indent: 1, elements: [section("nested")] },
{
type: "rich_text_section",
elements: [
{ type: "broadcast", range: "here" },
{ type: "text", text: " and " },
{ type: "usergroup", usergroup_id: "S0123456789" },
],
},
],
},
],
});
console.log("nodes :", JSON.stringify(message.formatted.children.map((c) => c.type)));
console.log("text :", JSON.stringify(message.text));
console.log("markdown:", JSON.stringify(stringifyMarkdown(message.formatted)));
Chat SDK Version
4.30.0
Node.js Version
22.21.1
Platform Adapter
Microsoft Teams, Slack
Operating System
Linux
Additional Context
No response
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at parseSlackMessage and slackMrkdwnToMarkdown, comparing how event.text is handled with the rich_text data in event.blocks; extractLinks shows the adapter already reads blocks. Run the supplied adapter.parseMessage example and verify that formatted contains the expected nested list, special mentions are readable, and stringifyMarkdown no longer emits literal bullets or an HTML node.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 75/100