mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-23 15:11:42 +00:00
135 lines
3.9 KiB
TypeScript
135 lines
3.9 KiB
TypeScript
import type { MarkdownTableData } from "openclaw/plugin-sdk/text-runtime";
|
|
|
|
const SLACK_MAX_TABLE_COLUMNS = 20;
|
|
const SLACK_MAX_TABLE_ROWS = 100;
|
|
const SLACK_MAX_FALLBACK_CELL_WIDTH = 80;
|
|
const SLACK_MAX_FALLBACK_TEXT_LENGTH = 4000;
|
|
|
|
type SlackTableCell = {
|
|
type: "raw_text";
|
|
text: string;
|
|
};
|
|
|
|
export type SlackTableBlock = {
|
|
type: "table";
|
|
column_settings: {
|
|
is_wrapped: boolean;
|
|
}[];
|
|
rows: SlackTableCell[][];
|
|
};
|
|
|
|
function hasVisibleHeaders(headers: string[]): boolean {
|
|
for (const header of headers) {
|
|
if (header.length > 0) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function getCappedRowCount(rows: string[][]): number {
|
|
return Math.min(rows.length, SLACK_MAX_TABLE_ROWS);
|
|
}
|
|
|
|
function getMaxColumnCount(headers: string[], rows: string[][]): number {
|
|
let maxColumns = headers.length;
|
|
const rowCount = getCappedRowCount(rows);
|
|
for (let index = 0; index < rowCount; index += 1) {
|
|
const rowLength = rows[index]?.length ?? 0;
|
|
if (rowLength > maxColumns) {
|
|
maxColumns = rowLength;
|
|
}
|
|
}
|
|
return Math.min(maxColumns, SLACK_MAX_TABLE_COLUMNS);
|
|
}
|
|
|
|
function truncateFallbackCell(value: string): string {
|
|
if (value.length <= SLACK_MAX_FALLBACK_CELL_WIDTH) {
|
|
return value;
|
|
}
|
|
return `${value.slice(0, SLACK_MAX_FALLBACK_CELL_WIDTH - 3)}...`;
|
|
}
|
|
|
|
export function markdownTableToSlackTableBlock(table: MarkdownTableData): SlackTableBlock {
|
|
const columnCount = getMaxColumnCount(table.headers, table.rows);
|
|
|
|
if (columnCount === 0) {
|
|
return { type: "table", column_settings: [], rows: [] };
|
|
}
|
|
|
|
const makeRow = (cells: string[]): SlackTableCell[] =>
|
|
Array.from({ length: columnCount }, (_, index) => ({
|
|
type: "raw_text",
|
|
text: cells[index] ?? "",
|
|
}));
|
|
|
|
const rows = [
|
|
...(hasVisibleHeaders(table.headers) ? [makeRow(table.headers)] : []),
|
|
...table.rows.slice(0, SLACK_MAX_TABLE_ROWS).map(makeRow),
|
|
].slice(0, SLACK_MAX_TABLE_ROWS);
|
|
|
|
return {
|
|
type: "table",
|
|
column_settings: Array.from({ length: columnCount }, () => ({ is_wrapped: true })),
|
|
rows,
|
|
};
|
|
}
|
|
|
|
export function buildSlackTableAttachment(table: MarkdownTableData): { blocks: SlackTableBlock[] } {
|
|
return {
|
|
blocks: [markdownTableToSlackTableBlock(table)],
|
|
};
|
|
}
|
|
|
|
export function renderSlackTableFallbackText(table: MarkdownTableData): string {
|
|
const hasHeaders = hasVisibleHeaders(table.headers);
|
|
const cappedRows = table.rows.slice(0, SLACK_MAX_TABLE_ROWS);
|
|
const rows = [...(hasHeaders ? [table.headers] : []), ...cappedRows].filter(
|
|
(row) => row.length > 0,
|
|
);
|
|
if (rows.length === 0) {
|
|
return "Table";
|
|
}
|
|
|
|
const columnCount = getMaxColumnCount(table.headers, cappedRows);
|
|
const widths = Array.from({ length: columnCount }, () => 1);
|
|
const safeRows = rows.map((row) =>
|
|
Array.from({ length: columnCount }, (_, columnIndex) =>
|
|
truncateFallbackCell(row[columnIndex] ?? ""),
|
|
),
|
|
);
|
|
|
|
for (const row of safeRows) {
|
|
for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {
|
|
const width = row[columnIndex]?.length ?? 0;
|
|
if (width > (widths[columnIndex] ?? 1)) {
|
|
widths[columnIndex] = width;
|
|
}
|
|
}
|
|
}
|
|
|
|
const lines: string[] = [];
|
|
let totalLength = 0;
|
|
for (let rowIndex = 0; rowIndex < safeRows.length; rowIndex += 1) {
|
|
const cells = Array.from({ length: columnCount }, (_, columnIndex) =>
|
|
(safeRows[rowIndex]?.[columnIndex] ?? "").padEnd(widths[columnIndex] ?? 1),
|
|
);
|
|
const line = `| ${cells.join(" | ")} |`;
|
|
if (totalLength + line.length > SLACK_MAX_FALLBACK_TEXT_LENGTH) {
|
|
break;
|
|
}
|
|
lines.push(line);
|
|
totalLength += line.length + 1;
|
|
if (rowIndex === 0 && hasHeaders) {
|
|
const separator = `| ${widths.map((width) => "-".repeat(width)).join(" | ")} |`;
|
|
if (totalLength + separator.length > SLACK_MAX_FALLBACK_TEXT_LENGTH) {
|
|
break;
|
|
}
|
|
lines.push(separator);
|
|
totalLength += separator.length + 1;
|
|
}
|
|
}
|
|
|
|
return lines.length > 0 ? lines.join("\n") : "Table";
|
|
}
|