export function escapeHtml(text: string): string { return text .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, ''') } function inlineToHtml(text: string): string { let html = escapeHtml(text) html = html.replace(/`([^`\n]+)`/g, '$1') html = html.replace(/\*\*([^*\n]+)\*\*/g, '$1') return html } /** Convert `> …` line groups into `
` (Telegram renders it with the accent-colored left bar). */ function blocksToHtml(text: string): string { const lines = text.split('\n') const out: string[] = [] let quote: string[] = [] const flush = (): void => { if (quote.length === 0) return out.push(`
${inlineToHtml(quote.join('\n'))}
`) quote = [] } for (const line of lines) { if (line.startsWith('> ')) quote.push(line.slice(2)) else if (line === '>') quote.push('') else { flush() out.push(inlineToHtml(line)) } } flush() return out.join('\n') } export function markdownToHtml(text: string): string { const parts = text.split(/```[\w-]*/) if (parts.length % 2 === 0) return inlineToHtml(text) let html = '' for (let i = 0; i < parts.length; i++) { if (i % 2 === 1) { const code = parts[i]!.replace(/^\n/, '').replace(/\n$/, '') html += `
${escapeHtml(code)}
` } else { html += blocksToHtml(parts[i]!) } } return html } export function splitMessage(text: string, maxLength: number): string[] { if (maxLength < 1) throw new Error('maxLength must be >= 1') if (text.length <= maxLength) return [text] const chunks: string[] = [] let rest = text while (rest.length > maxLength) { const window = rest.slice(0, maxLength) const newline = window.lastIndexOf('\n') const ideographic = window.lastIndexOf('。') const sentence = window.lastIndexOf('. ') const breakAt = Math.max(newline, ideographic, sentence) const cut = breakAt > 0 ? (breakAt === sentence ? breakAt + 2 : breakAt + 1) : maxLength chunks.push(rest.slice(0, cut)) rest = rest.slice(cut) } chunks.push(rest) return chunks }