teledzik 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -258,7 +258,7 @@ new HTML()
258
258
 
259
259
  // Nested formatting inside quote
260
260
  .blockQuote(
261
- HTML.bold('Telekaf') + ' supports ' + HTML.marked('highlighted') +
261
+ HTML.bold('Teledzik') + ' supports ' + HTML.marked('highlighted') +
262
262
  ' and ' + HTML.spoiler('spoiler') + ' text inside quotes.'
263
263
  )
264
264
  .build()
@@ -387,7 +387,7 @@ new HTML()
387
387
  ```ts
388
388
  new HTML()
389
389
  .paragraph(
390
- 'Telekaf ' + HTML.ref('note-1', '[1]') + ' is based on Telegraf ' + HTML.ref('note-2', '[2]') + '.'
390
+ 'Teledzik ' + HTML.ref('note-1', '[1]') + ' is based on Telegraf ' + HTML.ref('note-2', '[2]') + '.'
391
391
  )
392
392
  .divider()
393
393
  .referenceDefinition('note-1', HTML.url('https://npmjs.com/package/teledzik', 'teledzik on npm'))
@@ -1,30 +1,100 @@
1
1
  "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sanitizeRichHtml = void 0;
4
2
  /**
5
- * Converts Rich HTML strings containing tags like <h1>, <h2>, <p>, <hr>
3
+ * Converts Rich HTML strings (containing unsupported Telegram HTML tags like
4
+ * <table>, <ul>, <ol>, <li>, <details>, <h1>-<h6>, <aside>, <p>, <hr>, etc.)
6
5
  * into standard Telegram HTML compatible with parse_mode: 'HTML' (editMessageText/sendMessage).
7
6
  */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.sanitizeRichHtml = void 0;
9
+ function convertTableToText(tableHtml) {
10
+ const rows = [];
11
+ const rowMatches = tableHtml.match(/<tr[^>]*>([\s\S]*?)<\/tr>/gi) || [];
12
+ for (const row of rowMatches) {
13
+ const cells = [];
14
+ const cellMatches = row.match(/<(th|td)[^>]*>([\s\S]*?)<\/\1>/gi) || [];
15
+ for (const cell of cellMatches) {
16
+ const content = cell.replace(/<(th|td)[^>]*>([\s\S]*?)<\/\1>/i, '$2').trim();
17
+ cells.push(content);
18
+ }
19
+ if (cells.length > 0)
20
+ rows.push(cells);
21
+ }
22
+ if (rows.length === 0)
23
+ return '';
24
+ // Calculate column widths
25
+ const colWidths = [];
26
+ rows.forEach((r) => {
27
+ r.forEach((c, idx) => {
28
+ const textLen = c.replace(/<[^>]+>/g, '').length;
29
+ colWidths[idx] = Math.max(colWidths[idx] || 0, textLen);
30
+ });
31
+ });
32
+ // Format table rows
33
+ const formattedRows = rows.map((r, rowIdx) => {
34
+ const line = r
35
+ .map((c, colIdx) => {
36
+ const textLen = c.replace(/<[^>]+>/g, '').length;
37
+ const pad = ' '.repeat(Math.max(0, (colWidths[colIdx] || 0) - textLen));
38
+ return `${c}${pad}`;
39
+ })
40
+ .join(' │ ');
41
+ if (rowIdx === 0 && rows.length > 1) {
42
+ const sep = colWidths.map((w) => '─'.repeat(w)).join('─┼─');
43
+ return `${line}\n${sep}`;
44
+ }
45
+ return line;
46
+ });
47
+ return `<pre>\n${formattedRows.join('\n')}\n</pre>\n\n`;
48
+ }
8
49
  function sanitizeRichHtml(html) {
9
50
  if (!html)
10
51
  return html;
11
52
  let result = html;
12
- // Convert H1: <h1>Text</h1> -> <b>TEXT</b>
13
- result = result.replace(/<h1[^>]*>(.*?)<\/h1>/gis, (_, content) => `<b>${content.trim().toUpperCase()}</b>\n\n`);
14
- // Convert H2..H6: <h2>Text</h2> -> <b>Text</b>
15
- result = result.replace(/<h[2-6][^>]*>(.*?)<\/h[2-6]>/gis, (_, content) => `<b>${content.trim()}</b>\n\n`);
16
- // Convert Paragraphs: <p>Text</p> -> Text\n\n
17
- result = result.replace(/<p[^>]*>(.*?)<\/p>/gis, (_, content) => `${content.trim()}\n\n`);
18
- // Convert Footer: <footer>Text</footer> -> <i>Text</i>
19
- result = result.replace(/<footer[^>]*>(.*?)<\/footer>/gis, (_, content) => `<i>${content.trim()}</i>\n`);
20
- // Convert Horizontal Rules: <hr/> or <hr> -> ──────────
53
+ // 1. Convert Tables: <table>...</table> -> <pre>formatted table</pre>
54
+ result = result.replace(/<table[^>]*>([\s\S]*?)<\/table>/gi, (_, content) => convertTableToText(content));
55
+ // 2. Convert Details / Expandable: <details><summary>Title</summary>Content</details>
56
+ result = result.replace(/<details[^>]*>\s*<summary[^>]*>(.*?)<\/summary>([\s\S]*?)<\/details>/gi, (_, title, body) => {
57
+ return `<b>▶ ${title.trim()}</b>\n<blockquote>${body.trim()}</blockquote>\n\n`;
58
+ });
59
+ // 3. Convert Headings
60
+ result = result.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, (_, c) => `<b>${c.trim().toUpperCase()}</b>\n\n`);
61
+ result = result.replace(/<h[2-6][^>]*>([\s\S]*?)<\/h[2-6]>/gi, (_, c) => `<b>${c.trim()}</b>\n\n`);
62
+ // 4. Convert Task Lists & Unordered/Ordered Lists
63
+ result = result.replace(/<ul[^>]*>([\s\S]*?)<\/ul>/gi, (_, listContent) => {
64
+ return (listContent
65
+ .replace(/<li[^>]*>\s*<input[^>]*type="checkbox"[^>]*checked[^>]*>\s*([\s\S]*?)<\/li>/gi, '☑ $1\n')
66
+ .replace(/<li[^>]*>\s*<input[^>]*type="checkbox"[^>]*>\s*([\s\S]*?)<\/li>/gi, '☐ $1\n')
67
+ .replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, '• $1\n')
68
+ .trim() + '\n\n');
69
+ });
70
+ result = result.replace(/<ol[^>]*>([\s\S]*?)<\/ol>/gi, (_, listContent) => {
71
+ let index = 1;
72
+ return (listContent
73
+ .replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, (_match, item) => `${index++}. ${item.trim()}\n`)
74
+ .trim() + '\n\n');
75
+ });
76
+ // 5. Convert Pull Quotes / Asides
77
+ result = result.replace(/<aside[^>]*>([\s\S]*?)(?:<cite[^>]*>([\s\S]*?)<\/cite>)?<\/aside>/gi, (_, quote, cite) => {
78
+ return `<blockquote>${quote.trim()}${cite ? `\n— <i>${cite.trim()}</i>` : ''}</blockquote>\n\n`;
79
+ });
80
+ // 6. Convert Special Bot API 10.1 tags
81
+ result = result.replace(/<tg-thinking[^>]*>([\s\S]*?)<\/tg-thinking>/gi, (_, c) => `<blockquote>💭 <i>${c.trim()}</i></blockquote>\n\n`);
82
+ result = result.replace(/<tg-math-block[^>]*>([\s\S]*?)<\/tg-math-block>/gi, (_, c) => `<pre><code>${c.trim()}</code></pre>\n\n`);
83
+ result = result.replace(/<tg-reference[^>]*>([\s\S]*?)<\/tg-reference>/gi, (_, c) => `<i>${c.trim()}</i>`);
84
+ result = result.replace(/<mark[^>]*>([\s\S]*?)<\/mark>/gi, (_, c) => `<b>[${c.trim()}]</b>`);
85
+ // 7. Convert Figures & Captions
86
+ result = result.replace(/<figure[^>]*>[\s\S]*?<figcaption[^>]*>([\s\S]*?)<\/figcaption><\/figure>/gi, (_, cap) => `\n<i>📷 ${cap.trim()}</i>\n`);
87
+ // 8. Convert Paragraphs & Linebreaks
88
+ result = result.replace(/<p[^>]*>([\s\S]*?)<\/p>/gi, (_, c) => `${c.trim()}\n\n`);
89
+ result = result.replace(/<footer[^>]*>([\s\S]*?)<\/footer>/gi, (_, c) => `<i>${c.trim()}</i>\n`);
90
+ result = result.replace(/<br\s*\/?>/gi, '\n');
21
91
  result = result.replace(/<hr\s*\/?>/gi, '──────────\n');
22
- // Convert Marks: <mark>Text</mark> -> <b>[Text]</b>
23
- result = result.replace(/<mark[^>]*>(.*?)<\/mark>/gis, (_, content) => `<b>[${content.trim()}]</b>`);
24
- // Strip unsupported sub/sup tags but keep text
25
- result = result.replace(/<sub[^>]*>(.*?)<\/sub>/gis, '$1');
26
- result = result.replace(/<sup[^>]*>(.*?)<\/sup>/gis, '$1');
27
- // Trim extra trailing newlines (> 2 consecutive newlines)
92
+ // 9. Strip unsupported media & sub/sup tags but keep text
93
+ result = result.replace(/<sub[^>]*>([\s\S]*?)<\/sub>/gi, '$1');
94
+ result = result.replace(/<sup[^>]*>([\s\S]*?)<\/sup>/gi, '$1');
95
+ result = result.replace(/<tg-(map|collage|slideshow)[^>]*>([\s\S]*?)<\/tg-\1>/gi, '$2');
96
+ result = result.replace(/<(img|video|audio|tg-map)[^>]*\/?>/gi, '');
97
+ // 10. Clean up extra newlines (> 2 consecutive newlines)
28
98
  result = result.replace(/\n{3,}/g, '\n\n');
29
99
  return result.trim();
30
100
  }
package/lib/telegraf.js CHANGED
@@ -108,23 +108,34 @@ class Telegraf extends composer_1.Composer {
108
108
  const cyan = '\x1b[36m';
109
109
  const white = '\x1b[37m';
110
110
  console.log(`
111
- ${cyan}${bold} ╭─╮${reset}
112
- ${cyan}${bold} │ │${reset}
113
- ${cyan}${bold} ╰┬╯${reset}
114
- ${cyan}${bold} │ ╭─╮${reset}
115
- ${cyan}${bold} ╭┴╮ ╭────┤ │${reset}
116
- ${cyan}${bold} │ ├───╯ ╰─╯${reset}
117
- ${cyan}${bold} │ ├───╮${reset}
118
- ${cyan}${bold} ╰┬╯ │ ╭─╮${reset}
119
- ${cyan}${bold} │ ╰────┤ │${reset}
120
- ${cyan}${bold} ╭┴╮ ╰─╯${reset}
121
- ${cyan}${bold} │ │${reset}
122
- ${cyan}${bold} ╰─╯${reset}
111
+ ${cyan}⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠁⣬⣉⠛⣿⣿⣿⣿⠟⣉⠻⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
112
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⣸⣿⡟⢰⣿⣿⡿⢫⣶⣿⡷⢀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
113
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣖⣛⠻⠃⣾⣿⠟⣠⣿⣿⠟⣰⣿⣿⡿⠿⣿⣿⣿⣿⣿⣿⣿
114
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⡿⠿⠿⠿⠿⠿⠿⠿⣿⣿⣿⣾⣿⣿⣜⠛⠿⢃⣼⣿⡿⢋⣴⣷⣌⢻⣿⣿⣿⣿⣿
115
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⠋⠉⠀⢀⣀⣀⣠⣤⣤⣤⣤⣀⣀⠀⠀⠉⠛⠻⣿⣿⣿⣶⣿⡿⢋⣴⣿⣿⠟⣡⣾⣿⣿⣿⣿⣿
116
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠋⠁⠀⣀⣴⣶⡿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣄⡀⠀⠉⠻⣿⣿⣌⠻⣿⠟⣡⡾⢟⡉⢻⣿⣿⣿⣿
117
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠁⠀⣀⣴⣿⣿⡿⠋⣰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣮⡛⢷⣤⡀⠀⠙⢿⣷⣤⡞⣥⣼⣿⣿⡆⢹⣿⣿⣿
118
+ ⣿⣿⣿⣿⣿⣿⣿⣿⡿⠃⠀⣠⣾⣿⣿⣿⡿⠁⣼⣿⣿⡿⢿⣿⣿⣿⣿⣿⣿⣿⣿⡄⢹⣿⣿⡄⠙⣿⣦⡀⠀⠙⢿⣷⡉⠿⢋⣡⣴⣿⣿⣿⣿
119
+ ⣿⣿⣿⣿⣿⣿⣿⠟⠀⢀⣼⣿⣿⣿⣿⡿⠁⢸⡿⢻⣿⡇⢸⣿⣿⣿⣿⣿⣿⠿⣿⡇⠈⢿⣿⣿⣆⠈⢿⣿⣦⡀⠈⢿⣿⣶⣿⣿⣿⣿⣿⣿⣿
120
+ ⣿⣿⣿⣿⣿⣿⠏⠀⢠⣿⣿⣿⣿⣿⣿⠁⠀⣼⠃⢸⣿⡇⢸⣿⣿⣿⣿⣿⣿⠀⣿⡇⠀⠈⢿⣿⡿⠁⠀⢛⣯⣵⡄⠀⢿⣿⣿⣿⣿⣿⣿⣿⣿
121
+ ⣿⣿⣿⣿⣿⡟⠀⠀⠃⢸⣿⣿⣿⣿⡟⠀⣶⡟⠀⣘⣿⠇⠸⠟⢻⣿⣿⣿⣿⠀⣟⡇⢠⣆⠀⢲⣶⠀⠀⠘⣿⣿⣿⣄⠀⠉⠻⣿⣿⣿⣿⣿⣿
122
+ ⣿⣿⣿⣿⣿⡇⠀⠀⢠⣿⣿⣿⣿⣿⠃⢰⡟⢀⠀⠙⠛⠀⢀⠀⢸⣿⣿⣿⡇⢀⣄⠀⠘⠛⠃⠀⢻⡇⢀⠀⢸⣿⣿⡟⠳⣤⡀⠀⠉⠛⠛⠻⢿
123
+ ⣿⣿⣿⠿⠛⠀⠀⣠⠿⣿⣿⣿⣿⣿⠀⡼⠀⣸⡀⠐⠀⠀⠸⠀⣾⣿⣿⣿⠁⢸⡇⠀⣼⣶⣾⣇⠀⠃⢸⡄⢸⣿⣿⣿⡀⠈⠉⠓⠒⠀⠀⠀⣸
124
+ ⠁⠀⠀⠀⠀⠄⠚⠁⢸⣿⣿⣿⣿⣿⠀⠀⣸⣿⣧⣀⣴⣄⣀⣀⣉⣉⣁⣀⣀⣲⡤⠘⠏⣉⡿⠿⠃⠀⠋⢇⠀⣿⣿⣿⣇⠀⠀⠀⣶⣤⣶⣿⣿
125
+ ⣧⣤⣀⣀⡀⠀⣶⠀⣾⣿⣿⣿⣿⡿⠀⢀⠉⠉⠉⠛⠛⠛⠿⠿⣿⣿⣿⣿⣿⣿⡿⠒⠉⠀⢀⣠⣴⣶⣿⣿⠀⣿⢿⢿⣿⠀⠀⠀⢹⣿⣿⣿⣿
126
+ ⣿⣿⣿⣿⠃⠀⣿⠀⣿⣿⣇⣿⣿⣇⠀⣸⣿⣿⠷⠖⠒⠀⢀⣠⣼⣿⣿⣿⣿⣿⣦⣤⣤⣤⣤⣄⣀⣈⣹⣿⠐⣿⣸⢸⣿⠀⢰⠀⠘⣿⣿⣿⣿
127
+ ⣿⣿⣿⣿⡀⠀⢿⠀⢹⣿⣿⣿⣿⣿⠀⣏⣀⣀⣤⣴⣶⣿⣿⡿⠋⠉⠉⠉⠉⠉⠙⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆⠘⣿⣿⣿⠀⢸⠀⠀⣿⣿⣿⣿
128
+ ⣿⣿⣿⣿⡇⠀⢸⡄⠸⣿⣧⠏⢿⣿⠀⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⣿⣿⣿⣿⡇⢀⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠀⣿⣿⣿⠀⠘⠀⢀⣿⣿⣿⣿
129
+ ⣿⣿⣿⣿⣷⠀⠘⡇⠀⢿⣿⢀⠘⣿⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⠀⢻⣿⣿⣿⠃⢸⣿⣿⣿⣿⣿⣿⡿⠛⠁⢠⡞⠉⣿⡟⠀⠀⠀⣼⣿⣿⣿⣿
130
+ ⣿⣿⣿⣿⣿⣆⠀⠀⠀⠈⣿⣸⡆⠈⢇⠀⠉⠙⠻⠿⢿⣿⣿⣿⠀⢸⣿⣿⡟⠀⣾⠿⠿⠟⠛⠉⠁⢀⡀⠀⢸⠃⠀⡿⠀⠀⣀⣰⣿⣿⣿⣿⣿
131
+ ⣿⣿⣿⣿⣿⣿⣷⣤⣤⠀⠈⠋⣿⡆⠀⠀⠀⢠⣀⣀⠀⠀⠀⠀⠁⠈⠉⠀⠀⠀⠀⢀⣀⣤⣴⣶⣿⡇⠀⠀⠀⠀⠁⠀⣼⣿⣿⣿⣿⣿⣿⣿
132
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⡀⠀⠙⠧⠀⢀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⣤⣤⣄⣀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿
133
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣄⠀⠀⠀⣷⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿${reset}
134
+ ${bold}${white} teledzik${reset} ${dim}v1.0.0${reset}
135
+ ${dim} Terima kasih sudah menggunakan teledzik kami${reset}
136
+ ${dim} karena bagi kami itu sangat berharga${reset}
123
137
  ${reset}
124
- ${bold}${white} @icanseeuanywhere/telekaf${reset} ${dim}v4.16.8${reset}
125
- ${reset}
126
- ${dim} thanks for using my package${reset}
127
- ${dim} ~kafk6 ${cyan}t.me/kafk6${reset}
138
+ ${dim} 📩 Telegram: ${cyan}t.me/Lorddzik${reset}
128
139
  `);
129
140
  }
130
141
  get token() {
package/lib/telegram.js CHANGED
@@ -105,6 +105,9 @@ class Telegram extends client_1.default {
105
105
  */
106
106
  sendMessage(chatId, text, extra) {
107
107
  const t = format_1.FmtString.normalise(text);
108
+ if (extra?.parse_mode === 'HTML' && typeof t.text === 'string') {
109
+ t.text = (0, rich_sanitizer_1.sanitizeRichHtml)(t.text);
110
+ }
108
111
  return this.callApi('sendMessage', { chat_id: chatId, ...extra, ...t });
109
112
  }
110
113
  /**
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "teledzik",
3
- "version": "1.0.1",
4
- "description": "Modern Telegram Bot Framework (Bot API 1.0.1 + Rich Messages)",
3
+ "version": "1.0.3",
4
+ "description": "Modern Telegram Bot Framework",
5
5
  "keywords": [
6
6
  "telekaf",
7
+ "teledzik",
7
8
  "telegraf",
8
9
  "telegram",
9
10
  "telegram bot api",
@@ -65,7 +66,7 @@
65
66
  "main": "lib/index.js",
66
67
  "types": "./typings/index.d.ts",
67
68
  "bin": {
68
- "telekaf": "lib/cli.mjs"
69
+ "teledzik": "lib/cli.mjs"
69
70
  },
70
71
  "directories": {
71
72
  "lib": "lib"
@@ -1,35 +1,118 @@
1
1
  /**
2
- * Converts Rich HTML strings containing tags like <h1>, <h2>, <p>, <hr>
2
+ * Converts Rich HTML strings (containing unsupported Telegram HTML tags like
3
+ * <table>, <ul>, <ol>, <li>, <details>, <h1>-<h6>, <aside>, <p>, <hr>, etc.)
3
4
  * into standard Telegram HTML compatible with parse_mode: 'HTML' (editMessageText/sendMessage).
4
5
  */
6
+
7
+ function convertTableToText(tableHtml: string): string {
8
+ const rows: string[][] = []
9
+ const rowMatches = tableHtml.match(/<tr[^>]*>([\s\S]*?)<\/tr>/gi) || []
10
+
11
+ for (const row of rowMatches) {
12
+ const cells: string[] = []
13
+ const cellMatches = row.match(/<(th|td)[^>]*>([\s\S]*?)<\/\1>/gi) || []
14
+ for (const cell of cellMatches) {
15
+ const content = cell.replace(/<(th|td)[^>]*>([\s\S]*?)<\/\1>/i, '$2').trim()
16
+ cells.push(content)
17
+ }
18
+ if (cells.length > 0) rows.push(cells)
19
+ }
20
+
21
+ if (rows.length === 0) return ''
22
+
23
+ // Calculate column widths
24
+ const colWidths: number[] = []
25
+ rows.forEach((r) => {
26
+ r.forEach((c, idx) => {
27
+ const textLen = c.replace(/<[^>]+>/g, '').length
28
+ colWidths[idx] = Math.max(colWidths[idx] || 0, textLen)
29
+ })
30
+ })
31
+
32
+ // Format table rows
33
+ const formattedRows = rows.map((r, rowIdx) => {
34
+ const line = r
35
+ .map((c, colIdx) => {
36
+ const textLen = c.replace(/<[^>]+>/g, '').length
37
+ const pad = ' '.repeat(Math.max(0, (colWidths[colIdx] || 0) - textLen))
38
+ return `${c}${pad}`
39
+ })
40
+ .join(' │ ')
41
+
42
+ if (rowIdx === 0 && rows.length > 1) {
43
+ const sep = colWidths.map((w) => '─'.repeat(w)).join('─┼─')
44
+ return `${line}\n${sep}`
45
+ }
46
+ return line
47
+ })
48
+
49
+ return `<pre>\n${formattedRows.join('\n')}\n</pre>\n\n`
50
+ }
51
+
5
52
  export function sanitizeRichHtml(html: string): string {
6
53
  if (!html) return html
7
54
 
8
55
  let result = html
9
56
 
10
- // Convert H1: <h1>Text</h1> -> <b>TEXT</b>
11
- result = result.replace(/<h1[^>]*>(.*?)<\/h1>/gis, (_, content) => `<b>${content.trim().toUpperCase()}</b>\n\n`)
57
+ // 1. Convert Tables: <table>...</table> -> <pre>formatted table</pre>
58
+ result = result.replace(/<table[^>]*>([\s\S]*?)<\/table>/gi, (_, content) => convertTableToText(content))
12
59
 
13
- // Convert H2..H6: <h2>Text</h2> -> <b>Text</b>
14
- result = result.replace(/<h[2-6][^>]*>(.*?)<\/h[2-6]>/gis, (_, content) => `<b>${content.trim()}</b>\n\n`)
60
+ // 2. Convert Details / Expandable: <details><summary>Title</summary>Content</details>
61
+ result = result.replace(/<details[^>]*>\s*<summary[^>]*>(.*?)<\/summary>([\s\S]*?)<\/details>/gi, (_, title, body) => {
62
+ return `<b>▶ ${title.trim()}</b>\n<blockquote>${body.trim()}</blockquote>\n\n`
63
+ })
15
64
 
16
- // Convert Paragraphs: <p>Text</p> -> Text\n\n
17
- result = result.replace(/<p[^>]*>(.*?)<\/p>/gis, (_, content) => `${content.trim()}\n\n`)
65
+ // 3. Convert Headings
66
+ result = result.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, (_, c) => `<b>${c.trim().toUpperCase()}</b>\n\n`)
67
+ result = result.replace(/<h[2-6][^>]*>([\s\S]*?)<\/h[2-6]>/gi, (_, c) => `<b>${c.trim()}</b>\n\n`)
18
68
 
19
- // Convert Footer: <footer>Text</footer> -> <i>Text</i>
20
- result = result.replace(/<footer[^>]*>(.*?)<\/footer>/gis, (_, content) => `<i>${content.trim()}</i>\n`)
69
+ // 4. Convert Task Lists & Unordered/Ordered Lists
70
+ result = result.replace(/<ul[^>]*>([\s\S]*?)<\/ul>/gi, (_, listContent) => {
71
+ return (
72
+ listContent
73
+ .replace(/<li[^>]*>\s*<input[^>]*type="checkbox"[^>]*checked[^>]*>\s*([\s\S]*?)<\/li>/gi, '☑ $1\n')
74
+ .replace(/<li[^>]*>\s*<input[^>]*type="checkbox"[^>]*>\s*([\s\S]*?)<\/li>/gi, '☐ $1\n')
75
+ .replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, '• $1\n')
76
+ .trim() + '\n\n'
77
+ )
78
+ })
21
79
 
22
- // Convert Horizontal Rules: <hr/> or <hr> -> ──────────
23
- result = result.replace(/<hr\s*\/?>/gi, '──────────\n')
80
+ result = result.replace(/<ol[^>]*>([\s\S]*?)<\/ol>/gi, (_: string, listContent: string) => {
81
+ let index = 1
82
+ return (
83
+ listContent
84
+ .replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, (_match: string, item: string) => `${index++}. ${item.trim()}\n`)
85
+ .trim() + '\n\n'
86
+ )
87
+ })
88
+
89
+ // 5. Convert Pull Quotes / Asides
90
+ result = result.replace(/<aside[^>]*>([\s\S]*?)(?:<cite[^>]*>([\s\S]*?)<\/cite>)?<\/aside>/gi, (_, quote, cite) => {
91
+ return `<blockquote>${quote.trim()}${cite ? `\n— <i>${cite.trim()}</i>` : ''}</blockquote>\n\n`
92
+ })
24
93
 
25
- // Convert Marks: <mark>Text</mark> -> <b>[Text]</b>
26
- result = result.replace(/<mark[^>]*>(.*?)<\/mark>/gis, (_, content) => `<b>[${content.trim()}]</b>`)
94
+ // 6. Convert Special Bot API 10.1 tags
95
+ result = result.replace(/<tg-thinking[^>]*>([\s\S]*?)<\/tg-thinking>/gi, (_, c) => `<blockquote>💭 <i>${c.trim()}</i></blockquote>\n\n`)
96
+ result = result.replace(/<tg-math-block[^>]*>([\s\S]*?)<\/tg-math-block>/gi, (_, c) => `<pre><code>${c.trim()}</code></pre>\n\n`)
97
+ result = result.replace(/<tg-reference[^>]*>([\s\S]*?)<\/tg-reference>/gi, (_, c) => `<i>${c.trim()}</i>`)
98
+ result = result.replace(/<mark[^>]*>([\s\S]*?)<\/mark>/gi, (_, c) => `<b>[${c.trim()}]</b>`)
99
+
100
+ // 7. Convert Figures & Captions
101
+ result = result.replace(/<figure[^>]*>[\s\S]*?<figcaption[^>]*>([\s\S]*?)<\/figcaption><\/figure>/gi, (_, cap) => `\n<i>📷 ${cap.trim()}</i>\n`)
102
+
103
+ // 8. Convert Paragraphs & Linebreaks
104
+ result = result.replace(/<p[^>]*>([\s\S]*?)<\/p>/gi, (_, c) => `${c.trim()}\n\n`)
105
+ result = result.replace(/<footer[^>]*>([\s\S]*?)<\/footer>/gi, (_, c) => `<i>${c.trim()}</i>\n`)
106
+ result = result.replace(/<br\s*\/?>/gi, '\n')
107
+ result = result.replace(/<hr\s*\/?>/gi, '──────────\n')
27
108
 
28
- // Strip unsupported sub/sup tags but keep text
29
- result = result.replace(/<sub[^>]*>(.*?)<\/sub>/gis, '$1')
30
- result = result.replace(/<sup[^>]*>(.*?)<\/sup>/gis, '$1')
109
+ // 9. Strip unsupported media & sub/sup tags but keep text
110
+ result = result.replace(/<sub[^>]*>([\s\S]*?)<\/sub>/gi, '$1')
111
+ result = result.replace(/<sup[^>]*>([\s\S]*?)<\/sup>/gi, '$1')
112
+ result = result.replace(/<tg-(map|collage|slideshow)[^>]*>([\s\S]*?)<\/tg-\1>/gi, '$2')
113
+ result = result.replace(/<(img|video|audio|tg-map)[^>]*\/?>/gi, '')
31
114
 
32
- // Trim extra trailing newlines (> 2 consecutive newlines)
115
+ // 10. Clean up extra newlines (> 2 consecutive newlines)
33
116
  result = result.replace(/\n{3,}/g, '\n\n')
34
117
 
35
118
  return result.trim()
package/src/telegraf.ts CHANGED
@@ -164,23 +164,34 @@ export class Telegraf<C extends Context = Context> extends Composer<C> {
164
164
  const cyan = '\x1b[36m'
165
165
  const white = '\x1b[37m'
166
166
  console.log(`
167
- ${cyan}${bold} ╭─╮${reset}
168
- ${cyan}${bold} │ │${reset}
169
- ${cyan}${bold} ╰┬╯${reset}
170
- ${cyan}${bold} │ ╭─╮${reset}
171
- ${cyan}${bold} ╭┴╮ ╭────┤ │${reset}
172
- ${cyan}${bold} │ ├───╯ ╰─╯${reset}
173
- ${cyan}${bold} │ ├───╮${reset}
174
- ${cyan}${bold} ╰┬╯ │ ╭─╮${reset}
175
- ${cyan}${bold} │ ╰────┤ │${reset}
176
- ${cyan}${bold} ╭┴╮ ╰─╯${reset}
177
- ${cyan}${bold} │ │${reset}
178
- ${cyan}${bold} ╰─╯${reset}
167
+ ${cyan}⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠁⣬⣉⠛⣿⣿⣿⣿⠟⣉⠻⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
168
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⣸⣿⡟⢰⣿⣿⡿⢫⣶⣿⡷⢀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
169
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣖⣛⠻⠃⣾⣿⠟⣠⣿⣿⠟⣰⣿⣿⡿⠿⣿⣿⣿⣿⣿⣿⣿
170
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⡿⠿⠿⠿⠿⠿⠿⠿⣿⣿⣿⣾⣿⣿⣜⠛⠿⢃⣼⣿⡿⢋⣴⣷⣌⢻⣿⣿⣿⣿⣿
171
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⠋⠉⠀⢀⣀⣀⣠⣤⣤⣤⣤⣀⣀⠀⠀⠉⠛⠻⣿⣿⣿⣶⣿⡿⢋⣴⣿⣿⠟⣡⣾⣿⣿⣿⣿⣿
172
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠋⠁⠀⣀⣴⣶⡿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣄⡀⠀⠉⠻⣿⣿⣌⠻⣿⠟⣡⡾⢟⡉⢻⣿⣿⣿⣿
173
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠁⠀⣀⣴⣿⣿⡿⠋⣰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣮⡛⢷⣤⡀⠀⠙⢿⣷⣤⡞⣥⣼⣿⣿⡆⢹⣿⣿⣿
174
+ ⣿⣿⣿⣿⣿⣿⣿⣿⡿⠃⠀⣠⣾⣿⣿⣿⡿⠁⣼⣿⣿⡿⢿⣿⣿⣿⣿⣿⣿⣿⣿⡄⢹⣿⣿⡄⠙⣿⣦⡀⠀⠙⢿⣷⡉⠿⢋⣡⣴⣿⣿⣿⣿
175
+ ⣿⣿⣿⣿⣿⣿⣿⠟⠀⢀⣼⣿⣿⣿⣿⡿⠁⢸⡿⢻⣿⡇⢸⣿⣿⣿⣿⣿⣿⠿⣿⡇⠈⢿⣿⣿⣆⠈⢿⣿⣦⡀⠈⢿⣿⣶⣿⣿⣿⣿⣿⣿⣿
176
+ ⣿⣿⣿⣿⣿⣿⠏⠀⢠⣿⣿⣿⣿⣿⣿⠁⠀⣼⠃⢸⣿⡇⢸⣿⣿⣿⣿⣿⣿⠀⣿⡇⠀⠈⢿⣿⡿⠁⠀⢛⣯⣵⡄⠀⢿⣿⣿⣿⣿⣿⣿⣿⣿
177
+ ⣿⣿⣿⣿⣿⡟⠀⠀⠃⢸⣿⣿⣿⣿⡟⠀⣶⡟⠀⣘⣿⠇⠸⠟⢻⣿⣿⣿⣿⠀⣟⡇⢠⣆⠀⢲⣶⠀⠀⠘⣿⣿⣿⣄⠀⠉⠻⣿⣿⣿⣿⣿⣿
178
+ ⣿⣿⣿⣿⣿⡇⠀⠀⢠⣿⣿⣿⣿⣿⠃⢰⡟⢀⠀⠙⠛⠀⢀⠀⢸⣿⣿⣿⡇⢀⣄⠀⠘⠛⠃⠀⢻⡇⢀⠀⢸⣿⣿⡟⠳⣤⡀⠀⠉⠛⠛⠻⢿
179
+ ⣿⣿⣿⠿⠛⠀⠀⣠⠿⣿⣿⣿⣿⣿⠀⡼⠀⣸⡀⠐⠀⠀⠸⠀⣾⣿⣿⣿⠁⢸⡇⠀⣼⣶⣾⣇⠀⠃⢸⡄⢸⣿⣿⣿⡀⠈⠉⠓⠒⠀⠀⠀⣸
180
+ ⠁⠀⠀⠀⠀⠄⠚⠁⢸⣿⣿⣿⣿⣿⠀⠀⣸⣿⣧⣀⣴⣄⣀⣀⣉⣉⣁⣀⣀⣲⡤⠘⠏⣉⡿⠿⠃⠀⠋⢇⠀⣿⣿⣿⣇⠀⠀⠀⣶⣤⣶⣿⣿
181
+ ⣧⣤⣀⣀⡀⠀⣶⠀⣾⣿⣿⣿⣿⡿⠀⢀⠉⠉⠉⠛⠛⠛⠿⠿⣿⣿⣿⣿⣿⣿⡿⠒⠉⠀⢀⣠⣴⣶⣿⣿⠀⣿⢿⢿⣿⠀⠀⠀⢹⣿⣿⣿⣿
182
+ ⣿⣿⣿⣿⠃⠀⣿⠀⣿⣿⣇⣿⣿⣇⠀⣸⣿⣿⠷⠖⠒⠀⢀⣠⣼⣿⣿⣿⣿⣿⣦⣤⣤⣤⣤⣄⣀⣈⣹⣿⠐⣿⣸⢸⣿⠀⢰⠀⠘⣿⣿⣿⣿
183
+ ⣿⣿⣿⣿⡀⠀⢿⠀⢹⣿⣿⣿⣿⣿⠀⣏⣀⣀⣤⣴⣶⣿⣿⡿⠋⠉⠉⠉⠉⠉⠙⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆⠘⣿⣿⣿⠀⢸⠀⠀⣿⣿⣿⣿
184
+ ⣿⣿⣿⣿⡇⠀⢸⡄⠸⣿⣧⠏⢿⣿⠀⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⣿⣿⣿⣿⡇⢀⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠀⣿⣿⣿⠀⠘⠀⢀⣿⣿⣿⣿
185
+ ⣿⣿⣿⣿⣷⠀⠘⡇⠀⢿⣿⢀⠘⣿⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⠀⢻⣿⣿⣿⠃⢸⣿⣿⣿⣿⣿⣿⡿⠛⠁⢠⡞⠉⣿⡟⠀⠀⠀⣼⣿⣿⣿⣿
186
+ ⣿⣿⣿⣿⣿⣆⠀⠀⠀⠈⣿⣸⡆⠈⢇⠀⠉⠙⠻⠿⢿⣿⣿⣿⠀⢸⣿⣿⡟⠀⣾⠿⠿⠟⠛⠉⠁⢀⡀⠀⢸⠃⠀⡿⠀⠀⣀⣰⣿⣿⣿⣿⣿
187
+ ⣿⣿⣿⣿⣿⣿⣷⣤⣤⠀⠈⠋⣿⡆⠀⠀⠀⢠⣀⣀⠀⠀⠀⠀⠁⠈⠉⠀⠀⠀⠀⢀⣀⣤⣴⣶⣿⡇⠀⠀⠀⠀⠁⠀⣼⣿⣿⣿⣿⣿⣿⣿
188
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⡀⠀⠙⠧⠀⢀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⣤⣤⣄⣀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿
189
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣄⠀⠀⠀⣷⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿${reset}
190
+ ${bold}${white} teledzik${reset} ${dim}v1.0.0${reset}
191
+ ${dim} Terima kasih sudah menggunakan teledzik kami${reset}
192
+ ${dim} karena bagi kami itu sangat berharga${reset}
179
193
  ${reset}
180
- ${bold}${white} @icanseeuanywhere/telekaf${reset} ${dim}v4.16.8${reset}
181
- ${reset}
182
- ${dim} thanks for using my package${reset}
183
- ${dim} ~kafk6 ${cyan}t.me/kafk6${reset}
194
+ ${dim} 📩 Telegram: ${cyan}t.me/Lorddzik${reset}
184
195
  `)
185
196
  }
186
197
 
package/src/telegram.ts CHANGED
@@ -138,6 +138,9 @@ export class Telegram extends ApiClient {
138
138
  extra?: tt.ExtraReplyMessage
139
139
  ) {
140
140
  const t = FmtString.normalise(text)
141
+ if (extra?.parse_mode === 'HTML' && typeof t.text === 'string') {
142
+ t.text = sanitizeRichHtml(t.text)
143
+ }
141
144
  return this.callApi('sendMessage', { chat_id: chatId, ...extra, ...t })
142
145
  }
143
146
 
@@ -1,5 +1,6 @@
1
1
  /**
2
- * Converts Rich HTML strings containing tags like <h1>, <h2>, <p>, <hr>
2
+ * Converts Rich HTML strings (containing unsupported Telegram HTML tags like
3
+ * <table>, <ul>, <ol>, <li>, <details>, <h1>-<h6>, <aside>, <p>, <hr>, etc.)
3
4
  * into standard Telegram HTML compatible with parse_mode: 'HTML' (editMessageText/sendMessage).
4
5
  */
5
6
  export declare function sanitizeRichHtml(html: string): string;