teledzik 1.0.0

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.
Files changed (198) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +1126 -0
  3. package/filters.d.ts +1 -0
  4. package/filters.js +1 -0
  5. package/format.d.ts +1 -0
  6. package/format.js +1 -0
  7. package/future.d.ts +1 -0
  8. package/future.js +1 -0
  9. package/lib/button.js +101 -0
  10. package/lib/cli.mjs +105 -0
  11. package/lib/composer.js +581 -0
  12. package/lib/context.js +1300 -0
  13. package/lib/core/helpers/ai-stream.js +69 -0
  14. package/lib/core/helpers/args.js +58 -0
  15. package/lib/core/helpers/check.js +56 -0
  16. package/lib/core/helpers/compact.js +17 -0
  17. package/lib/core/helpers/deunionize.js +13 -0
  18. package/lib/core/helpers/formatting.js +89 -0
  19. package/lib/core/helpers/input-box.js +87 -0
  20. package/lib/core/helpers/rich-sanitizer.js +31 -0
  21. package/lib/core/helpers/smart-queue.js +70 -0
  22. package/lib/core/helpers/util.js +50 -0
  23. package/lib/core/network/client.js +318 -0
  24. package/lib/core/network/error.js +21 -0
  25. package/lib/core/network/multipart-stream.js +61 -0
  26. package/lib/core/network/polling.js +86 -0
  27. package/lib/core/network/webhook.js +54 -0
  28. package/lib/core/types/rich-message.js +287 -0
  29. package/lib/core/types/typegram.js +18 -0
  30. package/lib/filters.js +69 -0
  31. package/lib/format.js +38 -0
  32. package/lib/future.js +166 -0
  33. package/lib/index.js +61 -0
  34. package/lib/input.js +61 -0
  35. package/lib/markup.js +111 -0
  36. package/lib/middleware.js +2 -0
  37. package/lib/paginated.js +76 -0
  38. package/lib/reactions.js +81 -0
  39. package/lib/router.js +45 -0
  40. package/lib/scenes/base.js +39 -0
  41. package/lib/scenes/context.js +99 -0
  42. package/lib/scenes/form-scene.js +23 -0
  43. package/lib/scenes/index.js +21 -0
  44. package/lib/scenes/stage.js +49 -0
  45. package/lib/scenes/wizard/context.js +30 -0
  46. package/lib/scenes/wizard/index.js +45 -0
  47. package/lib/scenes.js +17 -0
  48. package/lib/session.js +163 -0
  49. package/lib/telegraf.js +274 -0
  50. package/lib/telegram-types.js +6 -0
  51. package/lib/telegram.js +1286 -0
  52. package/lib/types.js +2 -0
  53. package/lib/utils.js +5 -0
  54. package/markup.d.ts +1 -0
  55. package/markup.js +1 -0
  56. package/package.json +141 -0
  57. package/scenes.d.ts +1 -0
  58. package/scenes.js +1 -0
  59. package/session.d.ts +1 -0
  60. package/session.js +1 -0
  61. package/src/button.ts +182 -0
  62. package/src/composer.ts +1008 -0
  63. package/src/context.ts +1778 -0
  64. package/src/core/helpers/ai-stream.ts +96 -0
  65. package/src/core/helpers/args.ts +63 -0
  66. package/src/core/helpers/check.ts +71 -0
  67. package/src/core/helpers/compact.ts +18 -0
  68. package/src/core/helpers/deunionize.ts +26 -0
  69. package/src/core/helpers/formatting.ts +119 -0
  70. package/src/core/helpers/input-box.ts +86 -0
  71. package/src/core/helpers/rich-sanitizer.ts +36 -0
  72. package/src/core/helpers/smart-queue.ts +89 -0
  73. package/src/core/helpers/util.ts +96 -0
  74. package/src/core/network/client.ts +396 -0
  75. package/src/core/network/error.ts +29 -0
  76. package/src/core/network/multipart-stream.ts +45 -0
  77. package/src/core/network/polling.ts +94 -0
  78. package/src/core/network/webhook.ts +58 -0
  79. package/src/core/types/rich-message.ts +375 -0
  80. package/src/core/types/typegram.ts +44 -0
  81. package/src/filters.js +69 -0
  82. package/src/filters.ts +109 -0
  83. package/src/format.js +38 -0
  84. package/src/format.ts +110 -0
  85. package/src/future.js +147 -0
  86. package/src/future.ts +231 -0
  87. package/src/index.ts +27 -0
  88. package/src/input.ts +59 -0
  89. package/src/markup.js +93 -0
  90. package/src/markup.ts +142 -0
  91. package/src/middleware.ts +24 -0
  92. package/src/paginated.ts +75 -0
  93. package/src/reactions.ts +118 -0
  94. package/src/router.ts +55 -0
  95. package/src/scenes/base.ts +52 -0
  96. package/src/scenes/context.ts +136 -0
  97. package/src/scenes/form-scene.ts +34 -0
  98. package/src/scenes/index.ts +21 -0
  99. package/src/scenes/stage.ts +71 -0
  100. package/src/scenes/wizard/context.ts +58 -0
  101. package/src/scenes/wizard/index.ts +63 -0
  102. package/src/scenes.js +17 -0
  103. package/src/scenes.ts +1 -0
  104. package/src/session.js +175 -0
  105. package/src/session.ts +204 -0
  106. package/src/telegraf.ts +385 -0
  107. package/src/telegram-types.ts +256 -0
  108. package/src/telegram.ts +1700 -0
  109. package/src/types.js +2 -0
  110. package/src/types.ts +2 -0
  111. package/src/utils.js +5 -0
  112. package/src/utils.ts +1 -0
  113. package/types.d.ts +1 -0
  114. package/types.js +1 -0
  115. package/typings/button.d.ts +35 -0
  116. package/typings/button.d.ts.map +1 -0
  117. package/typings/composer.d.ts +226 -0
  118. package/typings/composer.d.ts.map +1 -0
  119. package/typings/context.d.ts +711 -0
  120. package/typings/context.d.ts.map +1 -0
  121. package/typings/core/helpers/ai-stream.d.ts +33 -0
  122. package/typings/core/helpers/args.d.ts +10 -0
  123. package/typings/core/helpers/args.d.ts.map +1 -0
  124. package/typings/core/helpers/check.d.ts +55 -0
  125. package/typings/core/helpers/check.d.ts.map +1 -0
  126. package/typings/core/helpers/compact.d.ts +3 -0
  127. package/typings/core/helpers/compact.d.ts.map +1 -0
  128. package/typings/core/helpers/deunionize.d.ts +17 -0
  129. package/typings/core/helpers/deunionize.d.ts.map +1 -0
  130. package/typings/core/helpers/formatting.d.ts +29 -0
  131. package/typings/core/helpers/formatting.d.ts.map +1 -0
  132. package/typings/core/helpers/input-box.d.ts +32 -0
  133. package/typings/core/helpers/rich-sanitizer.d.ts +5 -0
  134. package/typings/core/helpers/smart-queue.d.ts +18 -0
  135. package/typings/core/helpers/util.d.ts +26 -0
  136. package/typings/core/helpers/util.d.ts.map +1 -0
  137. package/typings/core/network/client.d.ts +54 -0
  138. package/typings/core/network/client.d.ts.map +1 -0
  139. package/typings/core/network/error.d.ts +15 -0
  140. package/typings/core/network/error.d.ts.map +1 -0
  141. package/typings/core/network/multipart-stream.d.ts +19 -0
  142. package/typings/core/network/multipart-stream.d.ts.map +1 -0
  143. package/typings/core/network/polling.d.ts +15 -0
  144. package/typings/core/network/polling.d.ts.map +1 -0
  145. package/typings/core/network/webhook.d.ts +6 -0
  146. package/typings/core/network/webhook.d.ts.map +1 -0
  147. package/typings/core/types/rich-message.d.ts +143 -0
  148. package/typings/core/types/rich-message.d.ts.map +1 -0
  149. package/typings/core/types/typegram.d.ts +36 -0
  150. package/typings/core/types/typegram.d.ts.map +1 -0
  151. package/typings/filters.d.ts +17 -0
  152. package/typings/filters.d.ts.map +1 -0
  153. package/typings/format.d.ts +21 -0
  154. package/typings/format.d.ts.map +1 -0
  155. package/typings/future.d.ts +11 -0
  156. package/typings/future.d.ts.map +1 -0
  157. package/typings/index.d.ts +21 -0
  158. package/typings/index.d.ts.map +1 -0
  159. package/typings/input.d.ts +58 -0
  160. package/typings/input.d.ts.map +1 -0
  161. package/typings/markup.d.ts +26 -0
  162. package/typings/markup.d.ts.map +1 -0
  163. package/typings/middleware.d.ts +7 -0
  164. package/typings/middleware.d.ts.map +1 -0
  165. package/typings/paginated.d.ts +20 -0
  166. package/typings/reactions.d.ts +31 -0
  167. package/typings/reactions.d.ts.map +1 -0
  168. package/typings/router.d.ts +20 -0
  169. package/typings/router.d.ts.map +1 -0
  170. package/typings/scenes/base.d.ts +21 -0
  171. package/typings/scenes/base.d.ts.map +1 -0
  172. package/typings/scenes/context.d.ts +35 -0
  173. package/typings/scenes/context.d.ts.map +1 -0
  174. package/typings/scenes/form-scene.d.ts +17 -0
  175. package/typings/scenes/index.d.ts +10 -0
  176. package/typings/scenes/index.d.ts.map +1 -0
  177. package/typings/scenes/stage.d.ts +23 -0
  178. package/typings/scenes/stage.d.ts.map +1 -0
  179. package/typings/scenes/wizard/context.d.ts +28 -0
  180. package/typings/scenes/wizard/context.d.ts.map +1 -0
  181. package/typings/scenes/wizard/index.d.ts +15 -0
  182. package/typings/scenes/wizard/index.d.ts.map +1 -0
  183. package/typings/scenes.d.ts +1 -0
  184. package/typings/scenes.d.ts.map +1 -0
  185. package/typings/session.d.ts +54 -0
  186. package/typings/session.d.ts.map +1 -0
  187. package/typings/telegraf.d.ts +118 -0
  188. package/typings/telegraf.d.ts.map +1 -0
  189. package/typings/telegram-types.d.ts +140 -0
  190. package/typings/telegram-types.d.ts.map +1 -0
  191. package/typings/telegram.d.ts +697 -0
  192. package/typings/telegram.d.ts.map +1 -0
  193. package/typings/types.d.ts +2 -0
  194. package/typings/types.d.ts.map +1 -0
  195. package/typings/utils.d.ts +1 -0
  196. package/typings/utils.d.ts.map +1 -0
  197. package/utils.d.ts +1 -0
  198. package/utils.js +1 -0
package/README.md ADDED
@@ -0,0 +1,1126 @@
1
+ <div align="center">
2
+
3
+ > Developed & maintained by **[@lorddzik](https://t.me/lorddzik)**
4
+
5
+ </div>
6
+
7
+ <header>
8
+
9
+ <div align="center">
10
+ <img src="docs/assets/logo.svg" alt="logo" height="90" align="center">
11
+ <h1 align="center">telegraf.js</h1>
12
+
13
+ <p>Modern Telegram Bot API framework for Node.js</p>
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" />
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" />
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" />
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" />
26
+ </a>
27
+ </div>
28
+
29
+ </header>
30
+
31
+ > **teledzik** (`teledzik`) is an up-to-date fork of [telegraf](https://github.com/telegraf/telegraf) — the popular Telegram Bot framework for Node.js.
32
+ > This package tracks the latest Telegram Bot API releases and ships features ahead of the upstream package.
33
+ > It is a drop-in replacement: just swap `npm install telegraf` → `npm install teledzik` and change your imports from `'telegraf'` to `'teledzik'`.
34
+
35
+
36
+ ---
37
+
38
+ ## 🆕 Rich Messages — Bot API 1.0.0
39
+
40
+ > Added in **Bot API 1.0.0** (June 11, 2026). Rich Messages let bots send highly structured content using HTML or Markdown — headings, lists, tables, media collages, code blocks, math expressions, AI thinking blocks, and more — with streaming support for AI-generated replies.
41
+
42
+ ### Table of Contents
43
+
44
+ - [Quick Start](#quick-start)
45
+ - [Sending Methods](#sending-methods)
46
+ - [InputRichMessage Format](#inputrichmessage-format)
47
+ - [RichHTMLBuilder](#richhtmlbuilder)
48
+ - [Text Formatting](#text-formatting-html)
49
+ - [Headings & Paragraphs](#headings--paragraphs)
50
+ - [Lists](#lists-html)
51
+ - [Code Blocks](#code-blocks)
52
+ - [Blockquote & Pull Quote](#blockquote--pull-quote)
53
+ - [Math](#math-html)
54
+ - [Media — Photo, Video, Audio](#media--photo-video-audio)
55
+ - [Collage & Slideshow](#collage--slideshow)
56
+ - [Map](#map)
57
+ - [Table](#table-html)
58
+ - [Details (Expandable)](#details-expandable)
59
+ - [Footnote References](#footnote-references-html)
60
+ - [Anchors & In-document Links](#anchors--in-document-links)
61
+ - [Special Elements](#special-elements)
62
+ - [RichMarkdownBuilder](#richmarkdownbuilder)
63
+ - [Streaming with sendRichMessageDraft](#streaming-with-sendrichmessagedraft)
64
+ - [reply\_markup with Rich Messages](#reply_markup-with-rich-messages)
65
+ - [Inline / Web App Queries](#inline--web-app-queries)
66
+ - [Exclusive Teledzik Enhancements](#exclusive-teledzik-enhancements)
67
+ - [Auto HTML Sanitizer & Edit Rich Message](#auto-html-sanitizer--edit-rich-message)
68
+ - [Smart Rate-Limit Queue](#smart-rate-limit-queue)
69
+ - [Live AI Streaming Adapter](#live-ai-streaming-adapter)
70
+ - [Declarative Form Builder](#declarative-form-builder)
71
+ - [Paginated Keyboard & List Builder](#paginated-keyboard--list-builder)
72
+ - [TypeScript](#typescript)
73
+
74
+ ---
75
+
76
+ ### Quick Start
77
+
78
+ ```ts
79
+ import { Telegraf, RichMessage } from 'teledzik'
80
+ const { RichHTMLBuilder: HTML } = RichMessage
81
+
82
+ const bot = new Telegraf(process.env.BOT_TOKEN)
83
+
84
+ bot.command('hello', async (ctx) => {
85
+ const msg = new HTML()
86
+ .heading(1, 'Hello World!')
87
+ .paragraph(HTML.bold('Rich Messages') + ' are now supported in Bot API 1.0.0.')
88
+ .divider()
89
+ .ul('Headings', 'Lists', 'Tables', 'Media', 'Math', 'and more')
90
+ .build()
91
+
92
+ await ctx.sendRichMessage(msg)
93
+ })
94
+
95
+ bot.launch()
96
+ process.once('SIGINT', () => bot.stop('SIGINT'))
97
+ process.once('SIGTERM', () => bot.stop('SIGTERM'))
98
+ ```
99
+
100
+ ---
101
+
102
+ ### Sending Methods
103
+
104
+ | Method | Description |
105
+ |---|---|
106
+ | `ctx.sendRichMessage(msg, extra?)` | Send a rich message to the current chat |
107
+ | `ctx.replyWithRichMessageContent(msg, extra?)` | Send a rich message quoting the current message |
108
+ | `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.sendRichMessageDraft(chatId, draftId, msg, extra?)` | Explicit streaming with chat ID |
111
+
112
+ **`extra` options for `sendRichMessage`:**
113
+
114
+ ```ts
115
+ await ctx.sendRichMessage(msg, {
116
+ message_thread_id: 123, // for forum topics
117
+ direct_messages_topic_id: 456, // for DM topics
118
+ disable_notification: true,
119
+ protect_content: true,
120
+ allow_paid_broadcast: false,
121
+ message_effect_id: 'effect_id',
122
+ reply_parameters: { message_id: ctx.message.message_id },
123
+ reply_markup: { inline_keyboard: [[{ text: 'OK', callback_data: 'ok' }]] },
124
+ })
125
+ ```
126
+
127
+ ---
128
+
129
+ ### InputRichMessage Format
130
+
131
+ `InputRichMessage` uses **either** `html` or `markdown` — not both:
132
+
133
+ ```ts
134
+ // HTML format
135
+ const msg: InputRichMessage = {
136
+ html: '<h1>Title</h1><p>Body text</p>',
137
+ is_rtl: false,
138
+ skip_entity_detection: false,
139
+ }
140
+
141
+ // Markdown format
142
+ const msg: InputRichMessage = {
143
+ markdown: '# Title\n\nBody text',
144
+ }
145
+ ```
146
+
147
+ Use the builders — `RichHTMLBuilder` or `RichMarkdownBuilder` — to construct these conveniently.
148
+
149
+ ---
150
+
151
+ ### RichHTMLBuilder
152
+
153
+ Import and instantiate:
154
+
155
+ ```ts
156
+ import { RichMessage } from 'teledzik'
157
+ const { RichHTMLBuilder: HTML } = RichMessage
158
+
159
+ const msg = new HTML()
160
+ .heading(1, 'Title')
161
+ .paragraph('Content')
162
+ .build() // returns InputRichMessage { html: '...' }
163
+ ```
164
+
165
+ #### Text Formatting (HTML)
166
+
167
+ Static inline helpers — return strings to embed inside block methods:
168
+
169
+ ```ts
170
+ HTML.bold('bold text') // <b>bold text</b>
171
+ HTML.italic('italic text') // <i>italic text</i>
172
+ HTML.underline('underlined') // <u>underlined</u>
173
+ HTML.strikethrough('crossed out') // <s>crossed out</s>
174
+ HTML.spoiler('hidden until tapped') // <tg-spoiler>hidden</tg-spoiler>
175
+ HTML.code('inline code') // <code>inline code</code>
176
+ HTML.marked('highlighted') // <mark>highlighted</mark>
177
+ HTML.sub('subscript') // <sub>subscript</sub>
178
+ HTML.sup('superscript') // <sup>superscript</sup>
179
+
180
+ HTML.url('https://t.me', 'Telegram') // <a href="...">Telegram</a>
181
+ HTML.email('hi@bot.com', 'Email us') // <a href="mailto:...">Email us</a>
182
+ HTML.phone('+6281234567', 'Call us') // <a href="tel:...">Call us</a>
183
+ HTML.mention(123456789, 'Alice') // <a href="tg://user?id=...">Alice</a>
184
+ HTML.customEmoji('5368324170671202286', '👍') // <tg-emoji emoji-id="...">👍</tg-emoji>
185
+ HTML.time(1647531900, 'wDT', '22:45 tomorrow') // <tg-time unix="..." format="...">...</tg-time>
186
+ HTML.inlineMath('E = mc^2') // $E = mc^2$
187
+
188
+ // Nesting
189
+ HTML.bold(HTML.italic('bold italic'))
190
+ HTML.underline(HTML.spoiler('underlined spoiler'))
191
+ ```
192
+
193
+ #### Headings & Paragraphs
194
+
195
+ ```ts
196
+ new HTML()
197
+ .heading(1, 'Main Title')
198
+ .heading(2, 'Subtitle')
199
+ .heading(3, HTML.bold('Bold heading'))
200
+ .heading(4, 'H4')
201
+ .heading(5, 'H5')
202
+ .heading(6, 'H6')
203
+ .paragraph('Normal paragraph text.')
204
+ .paragraph(HTML.bold('Bold') + ' and ' + HTML.italic('italic') + ' combined.')
205
+ .footer('Footer text — smaller and muted')
206
+ .divider() // <hr/>
207
+ .build()
208
+ ```
209
+
210
+ #### Lists (HTML)
211
+
212
+ ```ts
213
+ new HTML()
214
+ // Unordered list
215
+ .ul('First item', 'Second item', HTML.bold('Bold item'))
216
+
217
+ // Ordered list
218
+ .ol('Step one', 'Step two', 'Step three')
219
+
220
+ // Task list (checkboxes)
221
+ .taskList(
222
+ { text: 'Completed task', checked: true },
223
+ { text: 'Pending task', checked: false },
224
+ { text: HTML.bold('Important task'), checked: false },
225
+ )
226
+ .build()
227
+ ```
228
+
229
+ #### Code Blocks
230
+
231
+ ```ts
232
+ new HTML()
233
+ .pre('npm install teledzik', 'bash')
234
+ .pre('SELECT * FROM users WHERE active = 1;', 'sql')
235
+ .pre(
236
+ `const bot = new Telegraf(token)
237
+ bot.launch()`,
238
+ 'javascript'
239
+ )
240
+ .pre('plain preformatted block without language')
241
+ .build()
242
+ ```
243
+
244
+ Supported language identifiers: `javascript`, `typescript`, `python`, `bash`, `sql`, `json`, `html`, `css`, etc.
245
+
246
+ #### Blockquote & Pull Quote
247
+
248
+ ```ts
249
+ new HTML()
250
+ // Standard block quotation
251
+ .blockQuote(HTML.italic('"To be or not to be."'))
252
+
253
+ // Pull quotation with attribution (cite)
254
+ .pullQuote(
255
+ HTML.italic('"Design is not just what it looks like."'),
256
+ 'Steve Jobs'
257
+ )
258
+
259
+ // Nested formatting inside quote
260
+ .blockQuote(
261
+ HTML.bold('Telekaf') + ' supports ' + HTML.marked('highlighted') +
262
+ ' and ' + HTML.spoiler('spoiler') + ' text inside quotes.'
263
+ )
264
+ .build()
265
+ ```
266
+
267
+ #### Math (HTML)
268
+
269
+ ```ts
270
+ new HTML()
271
+ // Inline math (embed inside paragraph)
272
+ .paragraph(
273
+ 'The formula is: ' + HTML.inlineMath('a^2 + b^2 = c^2')
274
+ )
275
+
276
+ // Block math expression
277
+ .mathBlock('\\int_{-\\infty}^{\\infty} e^{-x^2} dx = \\sqrt{\\pi}')
278
+ .mathBlock('F(x) = \\int_{-\\infty}^{x} f(t)\\,dt')
279
+ .mathBlock('E = mc^2')
280
+ .build()
281
+ ```
282
+
283
+ #### Media — Photo, Video, Audio
284
+
285
+ ```ts
286
+ new HTML()
287
+ // Photo
288
+ .photo('https://example.com/photo.jpg')
289
+ .photo('https://example.com/photo.jpg', 'Caption text')
290
+ .photo('https://example.com/photo.jpg', 'Spoiler photo', /* spoiler */ true)
291
+
292
+ // Video
293
+ .video('https://example.com/video.mp4')
294
+ .video('https://example.com/video.mp4', 'Caption text')
295
+ .video('https://example.com/video.mp4', 'Spoiler video', /* spoiler */ true)
296
+
297
+ // Audio / Voice note (.ogg for voice)
298
+ .audio('https://example.com/audio.mp3')
299
+ .audio('https://example.com/audio.mp3', 'Audio caption')
300
+
301
+ // With figcaption (HTML figure)
302
+ .raw('<figure><img src="https://example.com/photo.jpg"/><figcaption>Caption <b>bold</b></figcaption></figure>')
303
+ .build()
304
+ ```
305
+
306
+ You can also use a Telegram `file_id` in place of a URL once the file is uploaded.
307
+
308
+ #### Collage & Slideshow
309
+
310
+ ```ts
311
+ const img = (src) => `<img src="${src}"/>`
312
+ const vid = (src) => `<video src="${src}"></video>`
313
+
314
+ new HTML()
315
+ // Collage — displays as a grid
316
+ .collage(
317
+ img('https://example.com/photo1.jpg'),
318
+ img('https://example.com/photo2.jpg'),
319
+ vid('https://example.com/clip.mp4'),
320
+ )
321
+
322
+ // Slideshow — swipeable carousel
323
+ .slideshow(
324
+ img('https://example.com/photo1.jpg'),
325
+ img('https://example.com/photo2.jpg'),
326
+ img('https://example.com/photo3.jpg'),
327
+ )
328
+ .build()
329
+ ```
330
+
331
+ #### Map
332
+
333
+ ```ts
334
+ new HTML()
335
+ .map(-6.2088, 106.8456) // Jakarta
336
+ .map(48.8584, 2.2945, 16) // Paris, zoom 16
337
+ .map(51.5074, -0.1278, 14, 'Our office in London') // with caption
338
+ .build()
339
+ ```
340
+
341
+ #### Table (HTML)
342
+
343
+ ```ts
344
+ new HTML()
345
+ .table(
346
+ [
347
+ ['Name', 'Version', 'Downloads'], // header row (hasHeader: true)
348
+ ['teledzik', '4.16.5', '—' ],
349
+ ['telegraf','4.16.3', '~120k/wk' ],
350
+ ],
351
+ { bordered: true, striped: true, hasHeader: true }
352
+ )
353
+ .build()
354
+ ```
355
+
356
+ For advanced table markup (colspan, rowspan, alignment), use `.raw()`:
357
+
358
+ ```ts
359
+ new HTML()
360
+ .raw(
361
+ '<table bordered striped>' +
362
+ '<tr><th>Name</th><th colspan="2">Details</th></tr>' +
363
+ '<tr><td>Alice</td><td align="center">98</td><td align="right">Pass</td></tr>' +
364
+ '</table>'
365
+ )
366
+ .build()
367
+ ```
368
+
369
+ #### Details (Expandable)
370
+
371
+ ```ts
372
+ new HTML()
373
+ // Collapsed by default
374
+ .details('Click to expand', '<p>Hidden content here.</p>')
375
+
376
+ // Open by default
377
+ .details(
378
+ HTML.bold('Changelog v4.16.5'),
379
+ '<ul><li>Fix InputRichMessage format</li><li>Add RichHTMLBuilder</li></ul>',
380
+ /* open */ true
381
+ )
382
+ .build()
383
+ ```
384
+
385
+ #### Footnote References (HTML)
386
+
387
+ ```ts
388
+ new HTML()
389
+ .paragraph(
390
+ 'Telekaf ' + HTML.ref('note-1', '[1]') + ' is based on Telegraf ' + HTML.ref('note-2', '[2]') + '.'
391
+ )
392
+ .divider()
393
+ .referenceDefinition('note-1', HTML.url('https://npmjs.com/package/teledzik', 'teledzik on npm'))
394
+ .referenceDefinition('note-2', HTML.url('https://github.com/telegraf/telegraf', 'telegraf on GitHub'))
395
+ .build()
396
+ ```
397
+
398
+ #### Anchors & In-document Links
399
+
400
+ ```ts
401
+ new HTML()
402
+ .raw(HTML.anchor('section-intro')) // invisible anchor target
403
+ .heading(2, 'Introduction')
404
+ .paragraph('Jump to: ' + HTML.anchorLink('section-api', 'API Reference'))
405
+ .raw(HTML.anchor('section-api'))
406
+ .heading(2, 'API Reference')
407
+ .build()
408
+ ```
409
+
410
+ #### Special Elements
411
+
412
+ ```ts
413
+ new HTML()
414
+ // AI thinking block (visible in sendRichMessageDraft)
415
+ .thinking(HTML.italic('Analyzing your request...'))
416
+
417
+ // Raw HTML for anything not covered by builder methods
418
+ .raw('<tg-map lat="41.9" long="12.5" zoom="14"/>')
419
+ .raw('<aside>Pull quote<cite>The Author</cite></aside>')
420
+ .build()
421
+ ```
422
+
423
+ ---
424
+
425
+ ### RichMarkdownBuilder
426
+
427
+ Same API surface as `RichHTMLBuilder` but produces Markdown output:
428
+
429
+ ```ts
430
+ import { RichMessage } from 'teledzik'
431
+ const { RichMarkdownBuilder: MD } = RichMessage
432
+
433
+ const msg = new MD()
434
+ .heading(1, 'Rich Markdown')
435
+ .paragraph(
436
+ MD.bold('bold') + ' ' +
437
+ MD.italic('italic') + ' ' +
438
+ MD.strikethrough('strike') + ' ' +
439
+ MD.marked('==highlighted==') + ' ' +
440
+ MD.spoiler('||spoiler||') + ' ' +
441
+ MD.code('`code`')
442
+ )
443
+ .divider()
444
+ .ul('Item 1', 'Item 2', MD.bold('Bold item'))
445
+ .ol('Step 1', 'Step 2', 'Step 3')
446
+ .taskList(
447
+ { text: 'Done', checked: true },
448
+ { text: 'Pending', checked: false },
449
+ )
450
+ .divider()
451
+ .pre('console.log("hello")', 'javascript')
452
+ .divider()
453
+ .table(
454
+ ['Name', 'Score'],
455
+ [['Alice', '98'], ['Bob', '87']],
456
+ ['left', 'center'],
457
+ )
458
+ .divider()
459
+ .mathBlock('E = mc^2')
460
+ .divider()
461
+ .blockQuote(MD.italic('"Quote text"'), '— Author')
462
+ .divider()
463
+ .photo('https://example.com/photo.jpg', 'Photo caption')
464
+ .collage('https://example.com/1.jpg', 'https://example.com/2.jpg')
465
+ .slideshow('https://example.com/1.jpg', 'https://example.com/2.jpg')
466
+ .divider()
467
+ .details('Expand me', '### Hidden heading\n\n- item 1\n- item 2')
468
+ .divider()
469
+ .paragraph('See footnote' + MD.sup('[1]'))
470
+ .footnote('1', MD.url('https://t.me/kafk6', '@kafka'))
471
+ .build()
472
+ ```
473
+
474
+ **All Markdown inline helpers:**
475
+
476
+ ```ts
477
+ MD.bold('text') // **text**
478
+ MD.italic('text') // *text*
479
+ MD.underline('text') // <u>text</u>
480
+ MD.strikethrough('text') // ~~text~~
481
+ MD.spoiler('text') // ||text||
482
+ MD.code('text') // `text`
483
+ MD.marked('text') // ==text==
484
+ MD.sub('text') // <sub>text</sub>
485
+ MD.sup('text') // <sup>text</sup>
486
+ MD.url('https://...', 'label') // [label](url)
487
+ MD.email('a@b.com', 'label') // [label](mailto:a@b.com)
488
+ MD.phone('+123', 'label') // [label](tel:+123)
489
+ MD.mention(123456789, 'Alice') // [Alice](tg://user?id=123456789)
490
+ MD.customEmoji('id', '👍') // ![👍](tg://emoji?id=...)
491
+ MD.time(1647531900, 'wDT') // ![](tg://time?unix=...&format=wDT)
492
+ MD.inlineMath('a^2') // $a^2$
493
+ ```
494
+
495
+ ---
496
+
497
+ ### Streaming with sendRichMessageDraft
498
+
499
+ `sendRichMessageDraft` streams a partial rich message in private chats. The draft is ephemeral (30-second preview). You **must** finalize with `sendRichMessage` to persist it.
500
+
501
+ - `chat_id` — private chat only (integer)
502
+ - `draft_id` — non-zero integer; updates with the same `draft_id` are animated
503
+
504
+ ```ts
505
+ bot.command('ai', async (ctx) => {
506
+ const DRAFT_ID = 1 // any non-zero integer
507
+
508
+ const steps = [
509
+ 'Reading your request...',
510
+ 'Searching knowledge base...',
511
+ 'Composing answer...',
512
+ ]
513
+
514
+ // Stream thinking blocks
515
+ for (const step of steps) {
516
+ await ctx.sendRichMessageDraft(
517
+ DRAFT_ID,
518
+ new HTML().thinking(HTML.italic(step)).build()
519
+ )
520
+ await new Promise((r) => setTimeout(r, 900))
521
+ }
522
+
523
+ // Finalize — must call sendRichMessage after streaming
524
+ await ctx.sendRichMessage(
525
+ new HTML()
526
+ .heading(2, '🤖 AI Response')
527
+ .paragraph('Here is the final answer from the AI.')
528
+ .divider()
529
+ .footer(HTML.url('https://t.me/kafk6', '@kafka'))
530
+ .build()
531
+ )
532
+ })
533
+ ```
534
+
535
+ You can also call `sendRichMessageDraft` explicitly:
536
+
537
+ ```ts
538
+ // Explicit
539
+ await ctx.telegram.sendRichMessageDraft(
540
+ ctx.chat.id, // private chat integer ID
541
+ 42, // draft_id
542
+ new HTML().thinking('Processing...').build(),
543
+ { message_thread_id: 123 }
544
+ )
545
+ ```
546
+
547
+ ---
548
+
549
+ ### reply_markup with Rich Messages
550
+
551
+ All `sendRichMessage` calls accept a `reply_markup` option with inline keyboards:
552
+
553
+ ```ts
554
+ import { Markup } from 'teledzik'
555
+
556
+ const msg = new HTML()
557
+ .heading(2, 'Choose an option')
558
+ .paragraph('Tap a button below:')
559
+ .build()
560
+
561
+ await ctx.sendRichMessage(msg, {
562
+ reply_markup: Markup.inlineKeyboard([
563
+ [
564
+ Markup.button.callback('✅ Yes', 'answer:yes'),
565
+ Markup.button.callback('❌ No', 'answer:no'),
566
+ ],
567
+ [Markup.button.url('🌐 Visit', 'https://t.me/kafk6')],
568
+ ]).reply_markup,
569
+ })
570
+
571
+ bot.action('answer:yes', async (ctx) => {
572
+ await ctx.answerCbQuery('You chose Yes!')
573
+ })
574
+ ```
575
+
576
+ Or use `reply_markup` directly:
577
+
578
+ ```ts
579
+ await ctx.sendRichMessage(msg, {
580
+ reply_markup: {
581
+ inline_keyboard: [
582
+ [{ text: 'Button 1', callback_data: 'btn1' }],
583
+ [{ text: 'Open URL', url: 'https://telegram.org' }],
584
+ ],
585
+ },
586
+ })
587
+ ```
588
+
589
+ ---
590
+
591
+ ### Inline / Web App Queries
592
+
593
+ Use `InputRichMessageContent` as `input_message_content` in inline query results:
594
+
595
+ ```ts
596
+ import { RichMessage } from 'teledzik'
597
+ const { RichHTMLBuilder: HTML } = RichMessage
598
+
599
+ bot.on('inline_query', async (ctx) => {
600
+ const richContent: RichMessage.InputRichMessageContent = {
601
+ rich_message: new HTML()
602
+ .heading(1, 'Result from Inline Query')
603
+ .paragraph('Sent via ' + HTML.url('https://t.me/kafk6', '@kafka') + '.')
604
+ .build(),
605
+ }
606
+
607
+ await ctx.answerInlineQuery([
608
+ {
609
+ type: 'article',
610
+ id: '1',
611
+ title: 'Rich Message Result',
612
+ input_message_content: richContent,
613
+ },
614
+ ])
615
+ })
616
+ ```
617
+
618
+ ---
619
+
620
+ ## Exclusive Teledzik Enhancements
621
+
622
+ ### Auto HTML Sanitizer & Edit Rich Message
623
+
624
+ 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>`.
625
+
626
+ `teledzik` automatically sanitizes HTML strings on `editMessageText` to convert headers to supported bold text, preventing API errors:
627
+
628
+ ```ts
629
+ // Safely edits text containing <h1>/<h2> without throwing 400 CANNOT_PARSE_ENTITIES!
630
+ await ctx.editMessageText('<h1>New Title</h1><p>Updated content</p>', { parse_mode: 'HTML' })
631
+
632
+ // Or use the native rich message edit helper:
633
+ await ctx.editRichMessage({ html: '<h1>Title</h1><p>Body</p>' })
634
+ ```
635
+
636
+ ### Smart Rate-Limit Queue
637
+
638
+ Prevent Telegram `429 Too Many Requests` errors automatically with sliding-window queueing and transparent auto-retries:
639
+
640
+ ```ts
641
+ await ctx.sendQueued(async () => {
642
+ return ctx.reply('Buffered message with rate-limit protection')
643
+ })
644
+ ```
645
+
646
+ ### Live AI Streaming Adapter
647
+
648
+ Stream LLM responses (Gemini, OpenAI, Claude, Ollama) directly to Telegram with automatic throttling (max update per 800ms) to avoid rate-limit bans:
649
+
650
+ ```ts
651
+ bot.on('text', async (ctx) => {
652
+ const stream = ctx.streamAI()
653
+
654
+ // Simulated LLM token stream
655
+ for await (const chunk of llmResponseStream) {
656
+ await stream.push(chunk)
657
+ }
658
+
659
+ await stream.end()
660
+ })
661
+ ```
662
+
663
+ ### Declarative Form Builder
664
+
665
+ Build step-by-step interactive wizards without verbose boilerplate:
666
+
667
+ ```ts
668
+ import { FormScene } from 'teledzik'
669
+
670
+ const profileForm = new FormScene('user-profile')
671
+ .field('name', { prompt: 'Please enter your name:' })
672
+ .field('age', { prompt: 'Please enter your age:', type: 'number' })
673
+ .onComplete((ctx, data) => {
674
+ ctx.reply(`Profile saved! Name: ${data.name}, Age: ${data.age}`)
675
+ })
676
+ ```
677
+
678
+ ### Paginated Keyboard & List Builder
679
+
680
+ Create paginated inline menus (`« Prev`, `Page X/Y`, `Next »`) automatically:
681
+
682
+ ```ts
683
+ import { PaginatedBuilder } from 'teledzik'
684
+
685
+ const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6']
686
+ const result = PaginatedBuilder.create(items, {
687
+ page: 1,
688
+ pageSize: 2,
689
+ formatItem: (item, idx) => `${idx + 1}. ${item}`,
690
+ })
691
+
692
+ await ctx.reply(result.text, { reply_markup: result.keyboard.reply_markup, parse_mode: 'HTML' })
693
+ ```
694
+
695
+ ### In-Chat Interactive Form Card (`InputBox`)
696
+
697
+ Render interactive boxed form cards in chat messages that update dynamically in real-time as users fill in field inputs:
698
+
699
+ ```ts
700
+ import { InputBox } from 'teledzik'
701
+
702
+ const formCard = new InputBox('user-form')
703
+ .setTitle('USER PROFILE CARD')
704
+ .addField('name', 'Full Name', { placeholder: 'Empty' })
705
+ .addField('email', 'Email Address', { placeholder: 'Empty' })
706
+
707
+ // Render card text & keyboard markup:
708
+ const cardText = formCard.renderText({ name: 'Alice' })
709
+ const keyboard = formCard.renderKeyboard({ name: 'Alice' })
710
+
711
+ await ctx.reply(cardText, { reply_markup: keyboard.reply_markup, parse_mode: 'HTML' })
712
+ ```
713
+
714
+ ---
715
+
716
+ ### TypeScript
717
+
718
+ All types are exported under the `RichMessage` namespace:
719
+
720
+ ```ts
721
+ import { RichMessage } from 'teledzik'
722
+ import type { ExtraSendRichMessage, ExtraSendRichMessageDraft } from 'teledzik/types'
723
+
724
+ // Builder types
725
+ const builder: RichMessage.RichHTMLBuilder = new RichMessage.RichHTMLBuilder()
726
+ const mdBuilder: RichMessage.RichMarkdownBuilder = new RichMessage.RichMarkdownBuilder()
727
+
728
+ // Message types
729
+ const input: RichMessage.InputRichMessage = { html: '<p>hello</p>' }
730
+ const content: RichMessage.InputRichMessageContent = { rich_message: input }
731
+ const received: RichMessage.RichMessage = ctx.message.rich_message
732
+
733
+ // Extra types
734
+ const extra: ExtraSendRichMessage = {
735
+ disable_notification: true,
736
+ reply_markup: { inline_keyboard: [] },
737
+ }
738
+
739
+ // Custom context with rich message
740
+ import { Context, Telegraf } from 'teledzik'
741
+ interface MyCtx extends Context {
742
+ session?: { lastDraftId: number }
743
+ }
744
+ const bot = new Telegraf<MyCtx>(process.env.BOT_TOKEN)
745
+ ```
746
+
747
+ ---
748
+
749
+ ## For 3.x users
750
+
751
+ - [3.x docs](https://telegraf.js.org/v3)
752
+ - [4.0 release notes](https://github.com/telegraf/telegraf/releases/tag/v4.0.0)
753
+
754
+ ## Introduction
755
+
756
+ Bots are special [Telegram](https://telegram.org) accounts designed to handle messages automatically.
757
+ Users can interact with bots by sending them command messages in private or group chats.
758
+ These accounts serve as an interface for code running somewhere on your server.
759
+
760
+ Telegraf is a library that makes it simple for you to develop your own Telegram bots using JavaScript or [TypeScript](https://www.typescriptlang.org/).
761
+
762
+ ### Features
763
+
764
+ - Full [Telegram Bot API 1.0.0](https://core.telegram.org/bots/api) support with **Rich Messages**
765
+ - [Excellent TypeScript typings](https://github.com/telegraf/telegraf/releases/tag/v4.0.0)
766
+ - [Lightweight](https://packagephobia.com/result?p=telegraf,node-telegram-bot-api)
767
+ - [AWS **λ**](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html)
768
+ / [Firebase](https://firebase.google.com/products/functions/)
769
+ / [Glitch](https://glitch.com/edit/#!/dashing-light)
770
+ / [Fly.io](https://fly.io/docs/languages-and-frameworks/node)
771
+ / Whatever ready
772
+ - `http/https/fastify/Connect.js/express.js` compatible webhooks
773
+ - Extensible
774
+
775
+ ### Example
776
+
777
+ ```js
778
+ const { Telegraf } = require('teledzik')
779
+ const { message } = require('teledzik/filters')
780
+
781
+ const bot = new Telegraf(process.env.BOT_TOKEN)
782
+ bot.start((ctx) => ctx.reply('Welcome'))
783
+ bot.help((ctx) => ctx.reply('Send me a sticker'))
784
+ bot.on(message('sticker'), (ctx) => ctx.reply('👍'))
785
+ bot.hears('hi', (ctx) => ctx.reply('Hey there'))
786
+ bot.launch()
787
+
788
+ // Enable graceful stop
789
+ process.once('SIGINT', () => bot.stop('SIGINT'))
790
+ process.once('SIGTERM', () => bot.stop('SIGTERM'))
791
+ ```
792
+
793
+ ```js
794
+ const { Telegraf } = require('teledzik')
795
+
796
+ const bot = new Telegraf(process.env.BOT_TOKEN)
797
+ bot.command('oldschool', (ctx) => ctx.reply('Hello'))
798
+ bot.command('hipster', Telegraf.reply('λ'))
799
+ bot.launch()
800
+
801
+ // Enable graceful stop
802
+ process.once('SIGINT', () => bot.stop('SIGINT'))
803
+ process.once('SIGTERM', () => bot.stop('SIGTERM'))
804
+ ```
805
+
806
+ For additional bot examples see the new [`docs repo`](https://github.com/feathers-studio/telegraf-docs/).
807
+
808
+ ### Resources
809
+
810
+ - [Getting started](#getting-started)
811
+ - [API reference](https://telegraf.js.org/modules.html)
812
+ - Telegram groups (sorted by number of members):
813
+ - [English](https://t.me/TelegrafJSChat)
814
+ - [Russian](https://t.me/telegrafjs_ru)
815
+ - [Uzbek](https://t.me/botjs_uz)
816
+ - [Ethiopian](https://t.me/telegraf_et)
817
+ - [GitHub Discussions](https://github.com/telegraf/telegraf/discussions)
818
+ - [Dependent repositories](https://libraries.io/npm/telegraf/dependent_repositories)
819
+
820
+ ## Getting started
821
+
822
+ ### Telegram token
823
+
824
+ To use the [Telegram Bot API](https://core.telegram.org/bots/api),
825
+ you first have to [get a bot account](https://core.telegram.org/bots)
826
+ by [chatting with BotFather](https://core.telegram.org/bots#6-botfather).
827
+
828
+ BotFather will give you a _token_, something like `123456789:AbCdefGhIJKlmNoPQRsTUVwxyZ`.
829
+
830
+ ### Installation
831
+
832
+ ```shellscript
833
+ $ npm install teledzik
834
+ ```
835
+
836
+ or
837
+
838
+ ```shellscript
839
+ $ yarn add teledzik
840
+ ```
841
+
842
+ or
843
+
844
+ ```shellscript
845
+ $ pnpm add teledzik
846
+ ```
847
+
848
+ ### `Telegraf` class
849
+
850
+ [`Telegraf`] instance represents your bot. It's responsible for obtaining updates and passing them to your handlers.
851
+
852
+ Start by [listening to commands](https://telegraf.js.org/classes/Telegraf-1.html#command) and [launching](https://telegraf.js.org/classes/Telegraf-1.html#launch) your bot.
853
+
854
+ ### `Context` class
855
+
856
+ `ctx` you can see in every example is a [`Context`] instance.
857
+ [`Telegraf`] creates one for each incoming update and passes it to your middleware.
858
+ It contains the `update`, `botInfo`, and `telegram` for making arbitrary Bot API requests,
859
+ as well as shorthand methods and getters.
860
+
861
+ This is probably the class you'll be using the most.
862
+
863
+ <!--
864
+ TODO: Verify and update list
865
+ Here is a list of
866
+
867
+ #### Known middleware
868
+
869
+ - [Internationalization](https://github.com/telegraf/telegraf-i18n)—simplifies selecting the right translation to use when responding to a user.
870
+ - [Redis powered session](https://github.com/telegraf/telegraf-session-redis)—store session data using Redis.
871
+ - [Local powered session (via lowdb)](https://github.com/RealSpeaker/telegraf-session-local)—store session data in a local file.
872
+ - [Rate-limiting](https://github.com/telegraf/telegraf-ratelimit)—apply rate limitting to chats or users.
873
+ - [Bottleneck powered throttling](https://github.com/KnightNiwrem/telegraf-throttler)—apply throttling to both incoming updates and outgoing API calls.
874
+ - [Menus via inline keyboards](https://github.com/EdJoPaTo/telegraf-inline-menu)—simplify creating interfaces based on menus.
875
+ - [Stateless Questions](https://github.com/EdJoPaTo/telegraf-stateless-question)—create stateless questions to Telegram users working in privacy mode.
876
+ - [Natural language processing via wit.ai](https://github.com/telegraf/telegraf-wit)
877
+ - [Natural language processing via recast.ai](https://github.com/telegraf/telegraf-recast)
878
+ - [Multivariate and A/B testing](https://github.com/telegraf/telegraf-experiments)—add experiments to see how different versions of a feature are used.
879
+ - [Powerfull bot stats via Mixpanel](https://github.com/telegraf/telegraf-mixpanel)
880
+ - [statsd integration](https://github.com/telegraf/telegraf-statsd)
881
+ - [and more...](https://www.npmjs.com/search?q=telegraf-)
882
+ -->
883
+
884
+ #### Shorthand methods
885
+
886
+ ```js
887
+ import { Telegraf } from 'teledzik'
888
+ import { message } from 'teledzik/filters'
889
+
890
+ const bot = new Telegraf(process.env.BOT_TOKEN)
891
+
892
+ bot.command('quit', async (ctx) => {
893
+ // Explicit usage
894
+ await ctx.telegram.leaveChat(ctx.message.chat.id)
895
+
896
+ // Using context shortcut
897
+ await ctx.leaveChat()
898
+ })
899
+
900
+ bot.on(message('text'), async (ctx) => {
901
+ // Explicit usage
902
+ await ctx.telegram.sendMessage(ctx.message.chat.id, `Hello ${ctx.state.role}`)
903
+
904
+ // Using context shortcut
905
+ await ctx.reply(`Hello ${ctx.state.role}`)
906
+ })
907
+
908
+ bot.on('callback_query', async (ctx) => {
909
+ // Explicit usage
910
+ await ctx.telegram.answerCbQuery(ctx.callbackQuery.id)
911
+
912
+ // Using context shortcut
913
+ await ctx.answerCbQuery()
914
+ })
915
+
916
+ bot.on('inline_query', async (ctx) => {
917
+ const result = []
918
+ // Explicit usage
919
+ await ctx.telegram.answerInlineQuery(ctx.inlineQuery.id, result)
920
+
921
+ // Using context shortcut
922
+ await ctx.answerInlineQuery(result)
923
+ })
924
+
925
+ bot.launch()
926
+
927
+ // Enable graceful stop
928
+ process.once('SIGINT', () => bot.stop('SIGINT'))
929
+ process.once('SIGTERM', () => bot.stop('SIGTERM'))
930
+ ```
931
+
932
+ ## Production
933
+
934
+ ### Webhooks
935
+
936
+ ```TS
937
+ import { Telegraf } from "teledzik";
938
+ import { message } from 'teledzik/filters';
939
+
940
+ const bot = new Telegraf(token);
941
+
942
+ bot.on(message("text"), ctx => ctx.reply("Hello"));
943
+
944
+ // Start webhook via launch method (preferred)
945
+ bot.launch({
946
+ webhook: {
947
+ // Public domain for webhook; e.g.: example.com
948
+ domain: webhookDomain,
949
+
950
+ // Port to listen on; e.g.: 8080
951
+ port: port,
952
+
953
+ // Optional path to listen for.
954
+ // `bot.secretPathComponent()` will be used by default
955
+ path: webhookPath,
956
+
957
+ // Optional secret to be sent back in a header for security.
958
+ // e.g.: `crypto.randomBytes(64).toString("hex")`
959
+ secretToken: randomAlphaNumericString,
960
+ },
961
+ });
962
+ ```
963
+
964
+ Use `createWebhook()` if you want to attach Telegraf to an existing http server.
965
+
966
+ <!-- global bot, tlsOptions -->
967
+
968
+ ```TS
969
+ import { createServer } from "http";
970
+
971
+ createServer(await bot.createWebhook({ domain: "example.com" })).listen(3000);
972
+ ```
973
+
974
+ ```TS
975
+ import { createServer } from "https";
976
+
977
+ createServer(tlsOptions, await bot.createWebhook({ domain: "example.com" })).listen(8443);
978
+ ```
979
+
980
+ - [AWS Lambda example integration](https://github.com/feathers-studio/telegraf-docs/tree/master/examples/functions/aws-lambda)
981
+ - [Google Cloud Functions example integration](https://github.com/feathers-studio/telegraf-docs/blob/master/examples/functions/google-cloud-function.ts)
982
+ - [`express` example integration](https://github.com/feathers-studio/telegraf-docs/blob/master/examples/webhook/express.ts)
983
+ - [`fastify` example integration](https://github.com/feathers-studio/telegraf-docs/blob/master/examples/webhook/fastify.ts)
984
+ - [`koa` example integration](https://github.com/feathers-studio/telegraf-docs/blob/master/examples/webhook/koa.ts)
985
+ - [NestJS framework integration module](https://github.com/bukhalo/nestjs-telegraf)
986
+ - [Cloudflare Workers integration module](https://github.com/Tsuk1ko/cfworker-middware-telegraf)
987
+ - Use [`bot.handleUpdate`](https://telegraf.js.org/classes/Telegraf-1.html#handleupdate) to write new integrations
988
+
989
+ ### Error handling
990
+
991
+ If middleware throws an error or times out, Telegraf calls `bot.handleError`. If it rethrows, update source closes, and then the error is printed to console and process terminates. If it does not rethrow, the error is swallowed.
992
+
993
+ Default `bot.handleError` always rethrows. You can overwrite it using `bot.catch` if you need to.
994
+
995
+ ⚠️ Swallowing unknown errors might leave the process in invalid state!
996
+
997
+ ℹ️ In production, `systemd` or [`pm2`](https://www.npmjs.com/package/pm2) can restart your bot if it exits for any reason.
998
+
999
+ ## Advanced topics
1000
+
1001
+ ### Working with files
1002
+
1003
+ Supported file sources:
1004
+
1005
+ - `Existing file_id`
1006
+ - `File path`
1007
+ - `Url`
1008
+ - `Buffer`
1009
+ - `ReadStream`
1010
+
1011
+ Also, you can provide an optional name of a file as `filename` when you send the file.
1012
+
1013
+ <!-- global bot, fs -->
1014
+
1015
+ ```js
1016
+ bot.on('message', async (ctx) => {
1017
+ // resend existing file by file_id
1018
+ await ctx.replyWithSticker('123123jkbhj6b')
1019
+
1020
+ // send file
1021
+ await ctx.replyWithVideo(Input.fromLocalFile('/path/to/video.mp4'))
1022
+
1023
+ // send stream
1024
+ await ctx.replyWithVideo(
1025
+ Input.fromReadableStream(fs.createReadStream('/path/to/video.mp4'))
1026
+ )
1027
+
1028
+ // send buffer
1029
+ await ctx.replyWithVoice(Input.fromBuffer(Buffer.alloc()))
1030
+
1031
+ // send url via Telegram server
1032
+ await ctx.replyWithPhoto(Input.fromURL('https://picsum.photos/200/300/'))
1033
+
1034
+ // pipe url content
1035
+ await ctx.replyWithPhoto(
1036
+ Input.fromURLStream('https://picsum.photos/200/300/?random', 'kitten.jpg')
1037
+ )
1038
+ })
1039
+ ```
1040
+
1041
+ ### Middleware
1042
+
1043
+ In addition to `ctx: Context`, each middleware receives `next: () => Promise<void>`.
1044
+
1045
+ As in Koa and some other middleware-based libraries,
1046
+ `await next()` will call next middleware and wait for it to finish:
1047
+
1048
+ ```TS
1049
+ import { Telegraf } from 'teledzik';
1050
+ import { message } from 'teledzik/filters';
1051
+
1052
+ const bot = new Telegraf(process.env.BOT_TOKEN);
1053
+
1054
+ bot.use(async (ctx, next) => {
1055
+ console.time(`Processing update ${ctx.update.update_id}`);
1056
+ await next() // runs next middleware
1057
+ // runs after next middleware finishes
1058
+ console.timeEnd(`Processing update ${ctx.update.update_id}`);
1059
+ })
1060
+
1061
+ bot.on(message('text'), (ctx) => ctx.reply('Hello World'));
1062
+ bot.launch();
1063
+
1064
+ // Enable graceful stop
1065
+ process.once('SIGINT', () => bot.stop('SIGINT'));
1066
+ process.once('SIGTERM', () => bot.stop('SIGTERM'));
1067
+ ```
1068
+
1069
+ With this simple ability, you can:
1070
+
1071
+ - extract information from updates and then `await next()` to avoid disrupting other middleware,
1072
+ - like [`Composer`] and [`Router`], `await next()` for updates you don't wish to handle,
1073
+ - like [`session`] and [`Scenes`], [extend the context](#extending-context) by mutating `ctx` before `await next()`,
1074
+ - [intercept API calls](https://github.com/telegraf/telegraf/discussions/1267#discussioncomment-254525),
1075
+ - reuse [other people's code](https://www.npmjs.com/search?q=telegraf-),
1076
+ - do whatever **you** come up with!
1077
+
1078
+ [`Telegraf`]: https://telegraf.js.org/classes/Telegraf-1.html
1079
+ [`Composer`]: https://telegraf.js.org/classes/Composer.html
1080
+ [`Context`]: https://telegraf.js.org/classes/Context.html
1081
+ [`Router`]: https://telegraf.js.org/classes/Router.html
1082
+ [`session`]: https://telegraf.js.org/modules.html#session
1083
+ [`Scenes`]: https://telegraf.js.org/modules/Scenes.html
1084
+
1085
+ ### Usage with TypeScript
1086
+
1087
+ Telegraf is written in TypeScript and therefore ships with declaration files for the entire library.
1088
+ Moreover, it includes types for the complete Telegram API via the [`typegram`](https://github.com/KnorpelSenf/typegram) package.
1089
+ While most types of Telegraf's API surface are self-explanatory, there's some notable things to keep in mind.
1090
+
1091
+ #### Extending `Context`
1092
+
1093
+ The exact shape of `ctx` can vary based on the installed middleware.
1094
+ Some custom middleware might register properties on the context object that Telegraf is not aware of.
1095
+ Consequently, you can change the type of `ctx` to fit your needs in order for you to have proper TypeScript types for your data.
1096
+ This is done through Generics:
1097
+
1098
+ ```ts
1099
+ import { Context, Telegraf } from 'teledzik'
1100
+
1101
+ // Define your own context type
1102
+ interface MyContext extends Context {
1103
+ myProp?: string
1104
+ myOtherProp?: number
1105
+ }
1106
+
1107
+ // Create your bot and tell it about your context type
1108
+ const bot = new Telegraf<MyContext>('SECRET TOKEN')
1109
+
1110
+ // Register middleware and launch your bot as usual
1111
+ bot.use((ctx, next) => {
1112
+ // Yay, `myProp` is now available here as `string | undefined`!
1113
+ ctx.myProp = ctx.chat?.first_name?.toUpperCase()
1114
+ return next()
1115
+ })
1116
+ // ...
1117
+ ```
1118
+
1119
+
1120
+ ---
1121
+
1122
+ ## Author & Maintainer
1123
+
1124
+ This fork is maintained by **@kafka** — [t.me/kafk6](https://t.me/kafk6)
1125
+
1126
+ Upstream project: [telegraf/telegraf](https://github.com/telegraf/telegraf) by The Telegraf Contributors.