Slack: inbound `<mailto:…|label>` and `<tel:…|label>` links are not converted, so the label ends up inside the link URL
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 2.4k
- Forks
- 314
- Avg merge
- 1d 18h
- Merged PRs (30d)
- 63
Description
Bug Description
slackMrkdwnToMarkdown converts Slack link tokens only when the target starts with http:// or https://. Slack uses the same <target|label> token for mailto: and tel: links, and it auto-links every email address a user types, so ordinary messages contain <mailto:a@b.com|a@b.com>.
The token passes through the converter unchanged. parseMarkdown then reads <mailto:a@b.com|a@b.com> as a CommonMark autolink, which produces a link node whose url is mailto:a@b.com|a@b.com. message.text contains mailto:a@b.com|a@b.com, and stringifyMarkdown(message.formatted) emits the raw Slack token.
Steps to Reproduce
- In Slack, send the bot a message that contains an email address, for example
mail a@b.com please. Slack deliverstext: "mail <mailto:a@b.com|a@b.com> please". - Read
message.textandstringifyMarkdown(message.formatted)in the handler, or run the code sample below, which calls the same converter the adapter uses.
Expected Behavior
A link node with url: "mailto:a@b.com" and text a@b.com, the same treatment <https://…|label> gets. message.text is mail a@b.com please. The same applies to <tel:+15551234567|555-123-4567> and to the unlabelled forms <mailto:a@b.com> and <tel:+15551234567>.
Actual Behavior
Input (event.text) |
link.url |
message.text |
stringifyMarkdown |
|---|---|---|---|
mail <mailto:a@b.com|a@b.com> please |
mailto:a@b.com|a@b.com |
mail mailto:a@b.com|a@b.com please |
mail <mailto:a@b.com|a@b.com> please |
call <tel:+15551234567|555-123-4567> now |
tel:+15551234567|555-123-4567 |
call tel:+15551234567|555-123-4567 now |
call <tel:+15551234567|555-123-4567> now |
mail <mailto:a@b.com> please |
mailto:a@b.com |
mail mailto:a@b.com please |
mail <mailto:a@b.com> please |
In the unlabelled case the URL is correct, but the visible text and message.text include the mailto: scheme.
Code Sample
import { SlackFormatConverter } from "@chat-adapter/slack";
import { stringifyMarkdown, toPlainText } from "chat";
const ast = new SlackFormatConverter().toAst("mail <mailto:a@b.com|a@b.com> please");
console.log(toPlainText(ast)); // "mail mailto:a@b.com|a@b.com please"
console.log(stringifyMarkdown(ast)); // "mail <mailto:a@b.com|a@b.com> please\n"
Chat SDK Version
4.30.0
Node.js Version
22.21.1
Platform Adapter
Microsoft Teams, Slack
Operating System
Linux
Additional Context
Workaround we run, applied to the mrkdwn before the converter in a SlackFormatConverter subclass:
const LABELLED_ADDRESS_LINK = /<((?:mailto|tel):[^|<>]+)\|([^<>]+)>/g;
const BARE_ADDRESS_LINK = /<((?:mailto|tel):([^|<>]+))>/g;
export function normalizeSlackMrkdwn(mrkdwn: string): string {
return mrkdwn.replace(LABELLED_ADDRESS_LINK, "[$2]($1)").replace(BARE_ADDRESS_LINK, "[$2]($1)");
}
export class NormalizingSlackFormatConverter extends SlackFormatConverter {
override toAst(mrkdwn: string): Root {
return super.toAst(normalizeSlackMrkdwn(mrkdwn));
}
}
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 SlackFormatConverter.toAst, which currently handles Slack link tokens only for HTTP targets. Reproduce the labelled and unlabelled mailto: and tel: examples from the issue, then verify that the resulting link URL, visible text, message.text, and stringifyMarkdown output match the expected behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 74/100