mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
feat: Add markdown table conversion for Slack formatting
This commit is contained in:
@@ -434,12 +434,89 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
||||
return { actionType: actionType as MicrosoftTeamsActionType, actionValue };
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts markdown tables to HTML tables for Teams MessageCard.
|
||||
* Teams MessageCard supports HTML in the text field.
|
||||
*/
|
||||
private static convertMarkdownTablesToHtml(markdown: string): string {
|
||||
// Regular expression to match markdown tables
|
||||
const tableRegex: RegExp =
|
||||
/(?:^|\n)((?:\|[^\n]+\|\n)+(?:\|[-:\s|]+\|\n)(?:\|[^\n]+\|\n?)+)/g;
|
||||
|
||||
return markdown.replace(
|
||||
tableRegex,
|
||||
(_match: string, table: string): string => {
|
||||
const lines: Array<string> = table.trim().split("\n");
|
||||
|
||||
if (lines.length < 2) {
|
||||
return table;
|
||||
}
|
||||
|
||||
// Parse header row
|
||||
const headerLine: string = lines[0] || "";
|
||||
const headers: Array<string> = headerLine
|
||||
.split("|")
|
||||
.map((cell: string) => {
|
||||
return cell.trim();
|
||||
})
|
||||
.filter((cell: string) => {
|
||||
return cell.length > 0;
|
||||
});
|
||||
|
||||
// Skip separator line (line with dashes) and get data rows
|
||||
const dataRows: Array<string> = lines.slice(2);
|
||||
|
||||
// Build HTML table
|
||||
let html: string =
|
||||
'<table style="border-collapse: collapse; width: 100%;">';
|
||||
|
||||
// Header row
|
||||
html += "<tr>";
|
||||
for (const header of headers) {
|
||||
html += `<th style="border: 1px solid #ddd; padding: 8px; background-color: #f2f2f2; text-align: left;"><strong>${header}</strong></th>`;
|
||||
}
|
||||
html += "</tr>";
|
||||
|
||||
// Data rows
|
||||
for (const row of dataRows) {
|
||||
const cells: Array<string> = row
|
||||
.split("|")
|
||||
.map((cell: string) => {
|
||||
return cell.trim();
|
||||
})
|
||||
.filter((cell: string) => {
|
||||
return cell.length > 0;
|
||||
});
|
||||
|
||||
if (cells.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
html += "<tr>";
|
||||
for (const cell of cells) {
|
||||
html += `<td style="border: 1px solid #ddd; padding: 8px;">${cell}</td>`;
|
||||
}
|
||||
html += "</tr>";
|
||||
}
|
||||
|
||||
html += "</table>";
|
||||
|
||||
return "\n" + html + "\n";
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
private static buildMessageCardFromMarkdown(markdown: string): JSONObject {
|
||||
/*
|
||||
* Teams MessageCard has limited markdown support. Headings like '##' are not supported
|
||||
* and single newlines can collapse. Convert common patterns to a structured card.
|
||||
*/
|
||||
const lines: Array<string> = markdown
|
||||
|
||||
// First, convert markdown tables to HTML
|
||||
const markdownWithHtmlTables: string =
|
||||
this.convertMarkdownTablesToHtml(markdown);
|
||||
|
||||
const lines: Array<string> = markdownWithHtmlTables
|
||||
.split("\n")
|
||||
.map((l: string) => {
|
||||
return l.trim();
|
||||
|
||||
@@ -1892,9 +1892,88 @@ export default class SlackUtil extends WorkspaceBase {
|
||||
return apiResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts markdown tables to a Slack-friendly format.
|
||||
* Since Slack's mrkdwn doesn't support tables, we convert them to
|
||||
* a row-by-row format with bold headers.
|
||||
*/
|
||||
private static convertMarkdownTablesToSlackFormat(markdown: string): string {
|
||||
// Regular expression to match markdown tables
|
||||
const tableRegex: RegExp =
|
||||
/(?:^|\n)((?:\|[^\n]+\|\n)+(?:\|[-:\s|]+\|\n)(?:\|[^\n]+\|\n?)+)/g;
|
||||
|
||||
return markdown.replace(
|
||||
tableRegex,
|
||||
(_match: string, table: string): string => {
|
||||
const lines: Array<string> = table.trim().split("\n");
|
||||
|
||||
if (lines.length < 2) {
|
||||
return table;
|
||||
}
|
||||
|
||||
// Parse header row
|
||||
const headerLine: string = lines[0] || "";
|
||||
const headers: Array<string> = headerLine
|
||||
.split("|")
|
||||
.map((cell: string) => {
|
||||
return cell.trim();
|
||||
})
|
||||
.filter((cell: string) => {
|
||||
return cell.length > 0;
|
||||
});
|
||||
|
||||
/*
|
||||
* Skip separator line (line with dashes)
|
||||
* Find data rows (skip header and separator)
|
||||
*/
|
||||
const dataRows: Array<string> = lines.slice(2);
|
||||
const formattedRows: Array<string> = [];
|
||||
|
||||
for (let rowIndex: number = 0; rowIndex < dataRows.length; rowIndex++) {
|
||||
const row: string = dataRows[rowIndex] || "";
|
||||
const cells: Array<string> = row
|
||||
.split("|")
|
||||
.map((cell: string) => {
|
||||
return cell.trim();
|
||||
})
|
||||
.filter((cell: string) => {
|
||||
return cell.length > 0;
|
||||
});
|
||||
|
||||
if (cells.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const rowParts: Array<string> = [];
|
||||
for (
|
||||
let cellIndex: number = 0;
|
||||
cellIndex < cells.length;
|
||||
cellIndex++
|
||||
) {
|
||||
const header: string =
|
||||
headers[cellIndex] || `Column ${cellIndex + 1}`;
|
||||
const value: string = cells[cellIndex] || "";
|
||||
rowParts.push(`*${header}:* ${value}`);
|
||||
}
|
||||
|
||||
if (dataRows.length > 1) {
|
||||
formattedRows.push(`_Row ${rowIndex + 1}_\n${rowParts.join("\n")}`);
|
||||
} else {
|
||||
formattedRows.push(rowParts.join("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
return "\n" + formattedRows.join("\n\n") + "\n";
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public static convertMarkdownToSlackRichText(markdown: string): string {
|
||||
return SlackifyMarkdown(markdown);
|
||||
// First convert tables to Slack-friendly format
|
||||
const markdownWithConvertedTables: string =
|
||||
this.convertMarkdownTablesToSlackFormat(markdown);
|
||||
return SlackifyMarkdown(markdownWithConvertedTables);
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
|
||||
Reference in New Issue
Block a user