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 +2 -2
- package/lib/core/helpers/rich-sanitizer.js +88 -18
- package/lib/telegraf.js +27 -16
- package/lib/telegram.js +3 -0
- package/package.json +4 -3
- package/src/core/helpers/rich-sanitizer.ts +100 -17
- package/src/telegraf.ts +27 -16
- package/src/telegram.ts +3 -0
- package/typings/core/helpers/rich-sanitizer.d.ts +2 -1
package/README.md
CHANGED
|
@@ -258,7 +258,7 @@ new HTML()
|
|
|
258
258
|
|
|
259
259
|
// Nested formatting inside quote
|
|
260
260
|
.blockQuote(
|
|
261
|
-
HTML.bold('
|
|
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
|
-
'
|
|
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
|
|
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
|
|
13
|
-
result = result.replace(/<
|
|
14
|
-
// Convert
|
|
15
|
-
result = result.replace(/<
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// Convert
|
|
19
|
-
result = result.replace(/<
|
|
20
|
-
|
|
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
|
-
//
|
|
23
|
-
result = result.replace(/<
|
|
24
|
-
|
|
25
|
-
result = result.replace(/<
|
|
26
|
-
result = result.replace(/<
|
|
27
|
-
//
|
|
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}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
${
|
|
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.
|
|
4
|
-
"description": "Modern Telegram Bot Framework
|
|
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
|
-
"
|
|
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
|
|
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
|
|
11
|
-
result = result.replace(/<
|
|
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
|
|
14
|
-
result = result.replace(/<
|
|
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
|
|
17
|
-
result = result.replace(/<
|
|
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
|
|
20
|
-
result = result.replace(/<
|
|
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
|
-
|
|
23
|
-
|
|
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
|
|
26
|
-
result = result.replace(/<
|
|
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[^>]*>(
|
|
30
|
-
result = result.replace(/<sup[^>]*>(
|
|
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
|
-
//
|
|
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}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
${
|
|
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
|
|
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;
|