teledzik 1.0.7 → 1.0.9

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
@@ -8,21 +8,21 @@
8
8
 
9
9
  <div align="center">
10
10
  <img src="docs/assets/logo.svg" alt="logo" height="90" align="center">
11
- <h1 align="center">telegraf.js</h1>
11
+ <h1 align="center">teledzik</h1>
12
12
 
13
- <p>Modern Telegram Bot API framework for Node.js</p>
13
+ <p>Modern Telegram Bot API framework for Node.js (with Native Rich Messages &amp; Full In-Place Edit)</p>
14
14
 
15
- <a href="https://core.telegram.org/bots/api">
16
- <img src="https://img.shields.io/badge/Bot%20API-v10.1-f36caf.svg?style=flat-square" alt="Bot API Version" />
15
+ <a href="https://www.npmjs.com/package/teledzik">
16
+ <img src="https://img.shields.io/npm/v/teledzik.svg?style=flat-square" alt="npm version" />
17
17
  </a>
18
- <a href="https://packagephobia.com/result?p=telegraf,node-telegram-bot-api">
19
- <img src="https://flat.badgen.net/packagephobia/install/telegraf" alt="install size" />
18
+ <a href="https://core.telegram.org/bots/api">
19
+ <img src="https://img.shields.io/badge/Bot%20API-v10.1+-f36caf.svg?style=flat-square" alt="Bot API Version" />
20
20
  </a>
21
- <a href="https://github.com/telegraf/telegraf">
22
- <img src="https://img.shields.io/github/languages/top/telegraf/telegraf?style=flat-square&logo=github" alt="GitHub top language" />
21
+ <a href="https://packagephobia.com/result?p=teledzik">
22
+ <img src="https://flat.badgen.net/packagephobia/install/teledzik" alt="install size" />
23
23
  </a>
24
- <a href="https://telegram.me/TelegrafJSChat">
25
- <img src="https://img.shields.io/badge/English%20chat-grey?style=flat-square&logo=telegram" alt="English chat" />
24
+ <a href="https://t.me/lorddzik">
25
+ <img src="https://img.shields.io/badge/Telegram-@lorddzik-blue?style=flat-square&logo=telegram" alt="Maintainer Telegram" />
26
26
  </a>
27
27
  </div>
28
28
 
@@ -99,14 +99,16 @@ process.once('SIGTERM', () => bot.stop('SIGTERM'))
99
99
 
100
100
  ---
101
101
 
102
- ### Sending Methods
102
+ ### Sending & Editing Methods
103
103
 
104
104
  | Method | Description |
105
105
  |---|---|
106
106
  | `ctx.sendRichMessage(msg, extra?)` | Send a rich message to the current chat |
107
107
  | `ctx.replyWithRichMessageContent(msg, extra?)` | Send a rich message quoting the current message |
108
+ | `ctx.editRichMessage(msg, extra?)` | In-place edit of message with native Rich UI preservation (v1.0.8+) |
108
109
  | `ctx.sendRichMessageDraft(draftId, msg, extra?)` | Stream a partial draft (private chats only) |
109
- | `ctx.telegram.sendRichMessage(chatId, msg, extra?)` | Explicit call with chat ID |
110
+ | `ctx.telegram.sendRichMessage(chatId, msg, extra?)` | Explicit send with chat ID |
111
+ | `ctx.telegram.editRichMessage(chatId, messageId, msg, extra?)` | Explicit edit with chat ID and message ID |
110
112
  | `ctx.telegram.sendRichMessageDraft(chatId, draftId, msg, extra?)` | Explicit streaming with chat ID |
111
113
 
112
114
  **`extra` options for `sendRichMessage`:**
@@ -645,17 +647,30 @@ bot.on('inline_query', async (ctx) => {
645
647
 
646
648
  ## Exclusive Teledzik Enhancements
647
649
 
648
- ### Auto HTML Sanitizer & Edit Rich Message
650
+ ### Native `editRichMessage` & Auto-Sanitizer (v1.0.8+)
649
651
 
650
- Standard Telegram Bot API's `editMessageText` (`parse_mode: 'HTML'`) fails with `400 CANNOT_PARSE_ENTITIES` when using block HTML tags like `<h1>`, `<h2>`, or `<p>`.
652
+ Standard `editMessageText` (`parse_mode: 'HTML'`) fails or degrades layout when editing complex structured blocks. In `teledzik` v1.0.8+, `ctx.editRichMessage` passes the payload natively via Telegram's `rich_message` parameter:
651
653
 
652
- `teledzik` automatically sanitizes HTML strings on `editMessageText` to convert headers to supported bold text, preventing API errors:
654
+ - **Preserves Full Rich UI**: In-place edits keep native `<table>`, `<h1>`-`<h6>`, collapsible cards (`<details>` / `<blockquote expandable>`), and styled elements without flattening them into plain text.
655
+ - **Smart Key-Value Table Handling**: 2-column tables correctly retain all rows and headers without accidental truncation.
656
+ - **Resilient Media & No-Op Handling**: Automatically falls back to `editMessageCaption` if editing a photo or video message, and silently resolves `message is not modified` instead of throwing unhandled exceptions.
653
657
 
654
658
  ```ts
655
- // Safely edits text containing <h1>/<h2> without throwing 400 CANNOT_PARSE_ENTITIES!
656
- await ctx.editMessageText('<h1>New Title</h1><p>Updated content</p>', { parse_mode: 'HTML' })
659
+ // 1. Edit with RichHTMLBuilder
660
+ const rich = new RichHTMLBuilder()
661
+ .heading(2, 'Order Confirmed')
662
+ .table([
663
+ ['Product', 'Status'],
664
+ ['VPS-01', 'ACTIVE 🟢']
665
+ ])
666
+
667
+ await ctx.editRichMessage(rich, {
668
+ reply_markup: Markup.inlineKeyboard([
669
+ [Markup.button.callback('Back to Menu', 'menu')]
670
+ ]).reply_markup
671
+ })
657
672
 
658
- // Or use the native rich message edit helper:
673
+ // 2. Edit with raw HTML / Rich Object
659
674
  await ctx.editRichMessage({ html: '<h1>Title</h1><p>Body</p>' })
660
675
  ```
661
676
 
@@ -3,8 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.sanitizeRichHtml = void 0;
4
4
  function formatTableToCard(tableHtml) {
5
5
  const rows = [];
6
+ let hasHeader = false;
6
7
  const rowMatches = tableHtml.match(/<tr[^>]*>([\s\S]*?)<\/tr>/gi) || [];
7
8
  for (const row of rowMatches) {
9
+ if (/<th[^>]*>/i.test(row))
10
+ hasHeader = true;
8
11
  const cells = [];
9
12
  const cellMatches = row.match(/<(th|td)[^>]*>([\s\S]*?)<\/\1>/gi) || [];
10
13
  for (const cell of cellMatches) {
@@ -17,15 +20,18 @@ function formatTableToCard(tableHtml) {
17
20
  if (rows.length === 0)
18
21
  return '';
19
22
  // Header vs Row handling (Format sebagai Kartu Elegan)
20
- const isKeyValue = rows.length > 1 && rows.every((r) => r.length === 2);
23
+ const isKeyValue = rows.length > 0 && rows.every((r) => r.length === 2);
21
24
  let cardText = '';
22
25
  if (isKeyValue) {
23
- const lines = rows.slice(1).map((r) => `• <b>${r[0]}:</b> ${r[1]}`);
26
+ const isHeaderRow = hasHeader && rows.length > 1 && (/^(parameter|informasi|metrik|field|key|unit|format|statistik|opsi|nama|item)$/i.test(rows[0][0].replace(/<[^>]*>/g, '').trim()) ||
27
+ /^(status|keterangan|detail|nilai|value|jumlah|estimasi|tipe|contoh input)$/i.test(rows[0][1].replace(/<[^>]*>/g, '').trim()));
28
+ const dataRows = isHeaderRow ? rows.slice(1) : rows;
29
+ const lines = dataRows.map((r) => `• <b>${r[0]}:</b> ${r[1]}`);
24
30
  cardText = `<blockquote>\n${lines.join('\n')}\n</blockquote>\n\n`;
25
31
  }
26
32
  else {
27
33
  const lines = rows.map((r, idx) => {
28
- if (idx === 0)
34
+ if (idx === 0 && hasHeader)
29
35
  return `<b>${r.join(' │ ')}</b>`;
30
36
  return `${r.join(' │ ')}`;
31
37
  });
package/lib/telegram.js CHANGED
@@ -1275,10 +1275,12 @@ class Telegram extends client_1.default {
1275
1275
  * Universal in-place message editor with Modern Rich UI formatting.
1276
1276
  */
1277
1277
  async editRichMessage(chatId, messageId, content, extra) {
1278
+ let richPayload = content;
1278
1279
  let htmlText = '';
1279
1280
  let replyMarkup = extra?.reply_markup;
1280
1281
  if (typeof content?.build === 'function') {
1281
1282
  const built = content.build();
1283
+ richPayload = built;
1282
1284
  if (typeof built === 'object' && built !== null) {
1283
1285
  htmlText = built.html || built.text || built.caption || '';
1284
1286
  if (built.reply_markup) {
@@ -1290,6 +1292,7 @@ class Telegram extends client_1.default {
1290
1292
  }
1291
1293
  }
1292
1294
  else if (typeof content === 'object' && content !== null) {
1295
+ richPayload = content;
1293
1296
  htmlText = content.html || content.text || content.caption || '';
1294
1297
  if (content.reply_markup) {
1295
1298
  replyMarkup = replyMarkup || content.reply_markup;
@@ -1297,14 +1300,12 @@ class Telegram extends client_1.default {
1297
1300
  }
1298
1301
  else if (typeof content === 'string') {
1299
1302
  htmlText = content;
1303
+ richPayload = content.startsWith('{') || content.includes('<') ? content : { text: content };
1300
1304
  }
1301
- // Format ke Telegram Modern HTML
1302
- htmlText = (0, rich_sanitizer_1.sanitizeRichHtml)(htmlText);
1303
1305
  const payload = {
1304
1306
  chat_id: chatId,
1305
1307
  message_id: Number(messageId),
1306
- text: htmlText,
1307
- parse_mode: 'HTML',
1308
+ rich_message: richPayload,
1308
1309
  ...extra,
1309
1310
  };
1310
1311
  if (replyMarkup)
@@ -1314,13 +1315,18 @@ class Telegram extends client_1.default {
1314
1315
  }
1315
1316
  catch (err) {
1316
1317
  const desc = String(err?.description || err?.message || '');
1318
+ // Ignore if message is unchanged
1319
+ if (desc.includes('message is not modified')) {
1320
+ return false;
1321
+ }
1322
+ const fallbackHtml = (0, rich_sanitizer_1.sanitizeRichHtml)(htmlText);
1317
1323
  // Fallback jika pesan berupa Media (Foto/Video)
1318
1324
  if (desc.includes('no text in the message') || desc.includes('message to edit not found')) {
1319
1325
  try {
1320
1326
  return await this.callApi('editMessageCaption', {
1321
1327
  chat_id: chatId,
1322
1328
  message_id: Number(messageId),
1323
- caption: htmlText,
1329
+ caption: fallbackHtml,
1324
1330
  parse_mode: 'HTML',
1325
1331
  reply_markup: replyMarkup,
1326
1332
  ...extra,
@@ -1332,8 +1338,16 @@ class Telegram extends client_1.default {
1332
1338
  throw captionErr;
1333
1339
  }
1334
1340
  }
1335
- if (desc.includes('message is not modified')) {
1336
- return false;
1341
+ // Fallback untuk Bot API versi lama tanpa parameter rich_message pada editMessageText
1342
+ if (desc.includes('rich_message') || desc.includes('not supported') || desc.includes('wrong type')) {
1343
+ return await this.callApi('editMessageText', {
1344
+ chat_id: chatId,
1345
+ message_id: Number(messageId),
1346
+ text: fallbackHtml,
1347
+ parse_mode: 'HTML',
1348
+ reply_markup: replyMarkup,
1349
+ ...extra,
1350
+ });
1337
1351
  }
1338
1352
  throw err;
1339
1353
  }
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "teledzik",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "Modern Telegram Bot Framework",
5
5
  "keywords": [
6
6
  "telekaf",
7
7
  "teledzik",
8
8
  "telegraf",
9
- "telegram",
9
+ "telegram",
10
10
  "telegram bot api",
11
11
  "bot",
12
12
  "botapi",
@@ -1,7 +1,9 @@
1
1
  function formatTableToCard(tableHtml: string): string {
2
2
  const rows: string[][] = []
3
+ let hasHeader = false
3
4
  const rowMatches = tableHtml.match(/<tr[^>]*>([\s\S]*?)<\/tr>/gi) || []
4
5
  for (const row of rowMatches) {
6
+ if (/<th[^>]*>/i.test(row)) hasHeader = true
5
7
  const cells: string[] = []
6
8
  const cellMatches = row.match(/<(th|td)[^>]*>([\s\S]*?)<\/\1>/gi) || []
7
9
  for (const cell of cellMatches) {
@@ -13,14 +15,19 @@ function formatTableToCard(tableHtml: string): string {
13
15
  if (rows.length === 0) return ''
14
16
 
15
17
  // Header vs Row handling (Format sebagai Kartu Elegan)
16
- const isKeyValue = rows.length > 1 && rows.every((r) => r.length === 2)
17
- let cardText = ''
18
+ const isKeyValue = rows.length > 0 && rows.every((r) => r.length === 2);
19
+ let cardText = '';
18
20
  if (isKeyValue) {
19
- const lines = rows.slice(1).map((r) => `• <b>${r[0]}:</b> ${r[1]}`)
20
- cardText = `<blockquote>\n${lines.join('\n')}\n</blockquote>\n\n`
21
+ const isHeaderRow = hasHeader && rows.length > 1 && (
22
+ /^(parameter|informasi|metrik|field|key|unit|format|statistik|opsi|nama|item)$/i.test(rows[0][0].replace(/<[^>]*>/g, '').trim()) ||
23
+ /^(status|keterangan|detail|nilai|value|jumlah|estimasi|tipe|contoh input)$/i.test(rows[0][1].replace(/<[^>]*>/g, '').trim())
24
+ );
25
+ const dataRows = isHeaderRow ? rows.slice(1) : rows;
26
+ const lines = dataRows.map((r) => `• <b>${r[0]}:</b> ${r[1]}`);
27
+ cardText = `<blockquote>\n${lines.join('\n')}\n</blockquote>\n\n`;
21
28
  } else {
22
29
  const lines = rows.map((r, idx) => {
23
- if (idx === 0) return `<b>${r.join(' │ ')}</b>`
30
+ if (idx === 0 && hasHeader) return `<b>${r.join(' │ ')}</b>`
24
31
  return `${r.join(' │ ')}`
25
32
  })
26
33
  cardText = `<blockquote>\n${lines.join('\n')}\n</blockquote>\n\n`
package/src/telegram.ts CHANGED
@@ -1688,11 +1688,13 @@ export class Telegram extends ApiClient {
1688
1688
  content: any,
1689
1689
  extra?: any
1690
1690
  ): Promise<any> {
1691
+ let richPayload: any = content
1691
1692
  let htmlText = ''
1692
1693
  let replyMarkup = extra?.reply_markup
1693
1694
 
1694
1695
  if (typeof content?.build === 'function') {
1695
1696
  const built = content.build()
1697
+ richPayload = built
1696
1698
  if (typeof built === 'object' && built !== null) {
1697
1699
  htmlText = built.html || built.text || built.caption || ''
1698
1700
  if (built.reply_markup) {
@@ -1702,22 +1704,20 @@ export class Telegram extends ApiClient {
1702
1704
  htmlText = String(built || '')
1703
1705
  }
1704
1706
  } else if (typeof content === 'object' && content !== null) {
1707
+ richPayload = content
1705
1708
  htmlText = content.html || content.text || content.caption || ''
1706
1709
  if (content.reply_markup) {
1707
1710
  replyMarkup = replyMarkup || content.reply_markup
1708
1711
  }
1709
1712
  } else if (typeof content === 'string') {
1710
1713
  htmlText = content
1714
+ richPayload = content.startsWith('{') || content.includes('<') ? content : { text: content }
1711
1715
  }
1712
1716
 
1713
- // Format ke Telegram Modern HTML
1714
- htmlText = sanitizeRichHtml(htmlText)
1715
-
1716
1717
  const payload: Record<string, any> = {
1717
1718
  chat_id: chatId,
1718
1719
  message_id: Number(messageId),
1719
- text: htmlText,
1720
- parse_mode: 'HTML',
1720
+ rich_message: richPayload,
1721
1721
  ...extra,
1722
1722
  }
1723
1723
 
@@ -1727,13 +1727,18 @@ export class Telegram extends ApiClient {
1727
1727
  return await this.callApi('editMessageText' as never, payload as never)
1728
1728
  } catch (err: any) {
1729
1729
  const desc = String(err?.description || err?.message || '')
1730
+ // Ignore if message is unchanged
1731
+ if (desc.includes('message is not modified')) {
1732
+ return false
1733
+ }
1734
+ const fallbackHtml = sanitizeRichHtml(htmlText)
1730
1735
  // Fallback jika pesan berupa Media (Foto/Video)
1731
1736
  if (desc.includes('no text in the message') || desc.includes('message to edit not found')) {
1732
1737
  try {
1733
1738
  return await this.callApi('editMessageCaption' as never, {
1734
1739
  chat_id: chatId,
1735
1740
  message_id: Number(messageId),
1736
- caption: htmlText,
1741
+ caption: fallbackHtml,
1737
1742
  parse_mode: 'HTML',
1738
1743
  reply_markup: replyMarkup,
1739
1744
  ...extra,
@@ -1743,8 +1748,16 @@ export class Telegram extends ApiClient {
1743
1748
  throw captionErr
1744
1749
  }
1745
1750
  }
1746
- if (desc.includes('message is not modified')) {
1747
- return false
1751
+ // Fallback untuk Bot API versi lama tanpa parameter rich_message pada editMessageText
1752
+ if (desc.includes('rich_message') || desc.includes('not supported') || desc.includes('wrong type')) {
1753
+ return await this.callApi('editMessageText' as never, {
1754
+ chat_id: chatId,
1755
+ message_id: Number(messageId),
1756
+ text: fallbackHtml,
1757
+ parse_mode: 'HTML',
1758
+ reply_markup: replyMarkup,
1759
+ ...extra,
1760
+ } as never)
1748
1761
  }
1749
1762
  throw err
1750
1763
  }