teh-bot 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 (5) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +560 -0
  3. package/index.d.ts +566 -0
  4. package/index.js +962 -0
  5. package/package.json +36 -0
package/index.js ADDED
@@ -0,0 +1,962 @@
1
+ const https = require("https")
2
+ const http = require("http")
3
+ const { EventEmitter } = require("events")
4
+ const { createReadStream, statSync, createWriteStream, promises: fsPromises } = require("fs")
5
+ const { basename, extname } = require("path")
6
+ const { URL } = require("url")
7
+ const { Stream, Readable } = require("stream")
8
+
9
+ const DEFAULT_OPTIONS = {
10
+ polling: false,
11
+ pollingInterval: 1000,
12
+ pollingTimeout: 30,
13
+ webhook: false,
14
+ webhookPort: 3000,
15
+ webhookPath: "/webhook",
16
+ requestTimeout: 30000,
17
+ maxConnections: 40,
18
+ allowedUpdates: [],
19
+ baseApiUrl: "https://api.telegram.org",
20
+ }
21
+
22
+ /**
23
+ * Modern, High-Performance Telegram Bot API Module
24
+ * Inspired by Baileys simplified API and Telegraf's robust system
25
+ */
26
+ class TelegramBot extends EventEmitter {
27
+ constructor(token, options = {}) {
28
+ super()
29
+
30
+ if (!token) throw new Error("[Teh] Bot token is required")
31
+
32
+ this.token = token
33
+ this.options = { ...DEFAULT_OPTIONS, ...options }
34
+ this.apiUrl = `${this.options.baseApiUrl}/bot${token}`
35
+
36
+ this.offset = 0
37
+ this.middleware = []
38
+ this.commands = new Map()
39
+ this.pollingActive = false
40
+ this.webhookServer = null
41
+
42
+ this._queue = []
43
+ this._isProcessing = false
44
+ this._rateLimitDelay = 0
45
+
46
+ if (this.options.polling) this.startPolling()
47
+ if (this.options.webhook) this.startWebhook()
48
+ }
49
+
50
+ async request(method, params = {}, formData = null) {
51
+ return new Promise((resolve, reject) => {
52
+ const url = new URL(`${this.apiUrl}/${method}`)
53
+ const headers = {}
54
+ let body
55
+
56
+ if (formData) {
57
+ const boundary = `----TehBoundary${Math.random().toString(36).substring(2)}`
58
+ headers["Content-Type"] = `multipart/form-data; boundary=${boundary}`
59
+ body = this._buildMultipartStream(formData, boundary)
60
+ } else {
61
+ headers["Content-Type"] = "application/json"
62
+ body = JSON.stringify(params)
63
+ }
64
+
65
+ const options = {
66
+ hostname: url.hostname,
67
+ port: url.port || 443,
68
+ path: url.pathname + url.search,
69
+ method: "POST",
70
+ headers: {
71
+ ...headers,
72
+ "User-Agent": "TehBot/1.0.0 (Modern; High-Performance)",
73
+ },
74
+ timeout: this.options.requestTimeout,
75
+ }
76
+
77
+ const req = https.request(options, (res) => {
78
+ const chunks = []
79
+ res.on("data", (chunk) => chunks.push(chunk))
80
+ res.on("end", () => {
81
+ const raw = Buffer.concat(chunks).toString()
82
+ try {
83
+ const response = JSON.parse(raw)
84
+ if (response.ok) resolve(response.result)
85
+ else reject(this._formatError(response))
86
+ } catch (e) {
87
+ reject(new Error(`[Teh] Parse Error: ${raw.substring(0, 100)}`))
88
+ }
89
+ })
90
+ })
91
+
92
+ req.on("error", reject)
93
+ if (body instanceof Stream) body.pipe(req)
94
+ else {
95
+ req.write(body)
96
+ req.end()
97
+ }
98
+ })
99
+ }
100
+
101
+ async sendMessage(chatId, content, options = {}) {
102
+ // content can be string (text) or object { image: ..., caption: ... }
103
+ if (typeof content === "string") {
104
+ return this.request("sendMessage", { chat_id: chatId, text: content, ...options })
105
+ }
106
+
107
+ const { image, video, audio, document, sticker, caption, ...rest } = content
108
+ const mediaType = image
109
+ ? "photo"
110
+ : video
111
+ ? "video"
112
+ : audio
113
+ ? "audio"
114
+ : document
115
+ ? "document"
116
+ : sticker
117
+ ? "sticker"
118
+ : null
119
+ const mediaSource = image || video || audio || document || sticker
120
+
121
+ if (!mediaType) return this.request("sendMessage", { chat_id: chatId, text: content.text, ...options })
122
+
123
+ const method = `send${mediaType.charAt(0).toUpperCase() + mediaType.slice(1)}`
124
+ const formData = { chat_id: chatId, caption, ...rest }
125
+
126
+ if (
127
+ mediaSource instanceof Stream ||
128
+ Buffer.isBuffer(mediaSource) ||
129
+ (typeof mediaSource === "string" && !mediaSource.startsWith("http"))
130
+ ) {
131
+ formData[mediaType] = await this._prepareFile(mediaSource, mediaType)
132
+ return this.request(method, {}, formData)
133
+ }
134
+
135
+ formData[mediaType] = mediaSource
136
+ return this.request(method, formData)
137
+ }
138
+
139
+ async sendPhoto(chatId, photo, options = {}) {
140
+ if (typeof photo === "string" && (photo.startsWith("http") || photo.startsWith("/"))) {
141
+ if (photo.startsWith("http")) {
142
+ return this.request("sendPhoto", {
143
+ chat_id: chatId,
144
+ photo,
145
+ ...options,
146
+ })
147
+ } else {
148
+ const fileData = await this._readFileSync(photo)
149
+ const formData = {
150
+ chat_id: chatId,
151
+ photo: {
152
+ stream: true,
153
+ filename: basename(photo),
154
+ contentType: "image/jpeg",
155
+ data: fileData,
156
+ },
157
+ ...this._flattenOptions(options),
158
+ }
159
+ return this.request("sendPhoto", {}, formData)
160
+ }
161
+ } else {
162
+ return this.request("sendPhoto", {
163
+ chat_id: chatId,
164
+ photo,
165
+ ...options,
166
+ })
167
+ }
168
+ }
169
+
170
+ async sendAudio(chatId, audio, options = {}) {
171
+ return this._sendFile("sendAudio", chatId, audio, "audio", options)
172
+ }
173
+
174
+ async sendDocument(chatId, document, options = {}) {
175
+ return this._sendFile("sendDocument", chatId, document, "document", options)
176
+ }
177
+
178
+ async sendVideo(chatId, video, options = {}) {
179
+ return this._sendFile("sendVideo", chatId, video, "video", options)
180
+ }
181
+
182
+ async sendAnimation(chatId, animation, options = {}) {
183
+ return this._sendFile("sendAnimation", chatId, animation, "animation", options)
184
+ }
185
+
186
+ async sendVoice(chatId, voice, options = {}) {
187
+ return this._sendFile("sendVoice", chatId, voice, "voice", options)
188
+ }
189
+
190
+ async sendVideoNote(chatId, videoNote, options = {}) {
191
+ return this._sendFile("sendVideoNote", chatId, videoNote, "video_note", options)
192
+ }
193
+
194
+ async sendSticker(chatId, sticker, options = {}) {
195
+ return this._sendFile("sendSticker", chatId, sticker, "sticker", options)
196
+ }
197
+
198
+ async sendLocation(chatId, latitude, longitude, options = {}) {
199
+ return this.request("sendLocation", {
200
+ chat_id: chatId,
201
+ latitude,
202
+ longitude,
203
+ ...options,
204
+ })
205
+ }
206
+
207
+ async sendVenue(chatId, latitude, longitude, title, address, options = {}) {
208
+ return this.request("sendVenue", {
209
+ chat_id: chatId,
210
+ latitude,
211
+ longitude,
212
+ title,
213
+ address,
214
+ ...options,
215
+ })
216
+ }
217
+
218
+ async sendContact(chatId, phoneNumber, firstName, options = {}) {
219
+ return this.request("sendContact", {
220
+ chat_id: chatId,
221
+ phone_number: phoneNumber,
222
+ first_name: firstName,
223
+ ...options,
224
+ })
225
+ }
226
+
227
+ async sendPoll(chatId, question, optionsArray, params = {}) {
228
+ return this.request("sendPoll", {
229
+ chat_id: chatId,
230
+ question,
231
+ options: optionsArray,
232
+ ...params,
233
+ })
234
+ }
235
+
236
+ async sendDice(chatId, options = {}) {
237
+ return this.request("sendDice", {
238
+ chat_id: chatId,
239
+ ...options,
240
+ })
241
+ }
242
+
243
+ async sendChatAction(chatId, action) {
244
+ return this.request("sendChatAction", {
245
+ chat_id: chatId,
246
+ action,
247
+ })
248
+ }
249
+
250
+ async forwardMessage(chatId, fromChatId, messageId, options = {}) {
251
+ return this.request("forwardMessage", {
252
+ chat_id: chatId,
253
+ from_chat_id: fromChatId,
254
+ message_id: messageId,
255
+ ...options,
256
+ })
257
+ }
258
+
259
+ async copyMessage(chatId, fromChatId, messageId, options = {}) {
260
+ return this.request("copyMessage", {
261
+ chat_id: chatId,
262
+ from_chat_id: fromChatId,
263
+ message_id: messageId,
264
+ ...options,
265
+ })
266
+ }
267
+
268
+ async editMessageText(text, options = {}) {
269
+ return this.request("editMessageText", {
270
+ text,
271
+ ...options,
272
+ })
273
+ }
274
+
275
+ async editMessageCaption(options = {}) {
276
+ return this.request("editMessageCaption", options)
277
+ }
278
+
279
+ async editMessageReplyMarkup(options = {}) {
280
+ return this.request("editMessageReplyMarkup", options)
281
+ }
282
+
283
+ async deleteMessage(chatId, messageId) {
284
+ return this.request("deleteMessage", {
285
+ chat_id: chatId,
286
+ message_id: messageId,
287
+ })
288
+ }
289
+
290
+ async answerCallbackQuery(callbackQueryId, options = {}) {
291
+ return this.request("answerCallbackQuery", {
292
+ callback_query_id: callbackQueryId,
293
+ ...options,
294
+ })
295
+ }
296
+
297
+ async answerInlineQuery(inlineQueryId, results, options = {}) {
298
+ return this.request("answerInlineQuery", {
299
+ inline_query_id: inlineQueryId,
300
+ results,
301
+ ...options,
302
+ })
303
+ }
304
+
305
+ async getChat(chatId) {
306
+ return this.request("getChat", { chat_id: chatId })
307
+ }
308
+
309
+ async getChatAdministrators(chatId) {
310
+ return this.request("getChatAdministrators", { chat_id: chatId })
311
+ }
312
+
313
+ async getChatMemberCount(chatId) {
314
+ return this.request("getChatMemberCount", { chat_id: chatId })
315
+ }
316
+
317
+ async getChatMember(chatId, userId) {
318
+ return this.request("getChatMember", {
319
+ chat_id: chatId,
320
+ user_id: userId,
321
+ })
322
+ }
323
+
324
+ async setChatTitle(chatId, title) {
325
+ return this.request("setChatTitle", {
326
+ chat_id: chatId,
327
+ title,
328
+ })
329
+ }
330
+
331
+ async setChatDescription(chatId, description) {
332
+ return this.request("setChatDescription", {
333
+ chat_id: chatId,
334
+ description,
335
+ })
336
+ }
337
+
338
+ async pinChatMessage(chatId, messageId, options = {}) {
339
+ return this.request("pinChatMessage", {
340
+ chat_id: chatId,
341
+ message_id: messageId,
342
+ ...options,
343
+ })
344
+ }
345
+
346
+ async unpinChatMessage(chatId, options = {}) {
347
+ return this.request("unpinChatMessage", {
348
+ chat_id: chatId,
349
+ ...options,
350
+ })
351
+ }
352
+
353
+ async unpinAllChatMessages(chatId) {
354
+ return this.request("unpinAllChatMessages", { chat_id: chatId })
355
+ }
356
+
357
+ async leaveChat(chatId) {
358
+ return this.request("leaveChat", { chat_id: chatId })
359
+ }
360
+
361
+ async banChatMember(chatId, userId, options = {}) {
362
+ return this.request("banChatMember", {
363
+ chat_id: chatId,
364
+ user_id: userId,
365
+ ...options,
366
+ })
367
+ }
368
+
369
+ async unbanChatMember(chatId, userId, options = {}) {
370
+ return this.request("unbanChatMember", {
371
+ chat_id: chatId,
372
+ user_id: userId,
373
+ ...options,
374
+ })
375
+ }
376
+
377
+ async restrictChatMember(chatId, userId, permissions, options = {}) {
378
+ return this.request("restrictChatMember", {
379
+ chat_id: chatId,
380
+ user_id: userId,
381
+ permissions,
382
+ ...options,
383
+ })
384
+ }
385
+
386
+ async promoteChatMember(chatId, userId, options = {}) {
387
+ return this.request("promoteChatMember", {
388
+ chat_id: chatId,
389
+ user_id: userId,
390
+ ...options,
391
+ })
392
+ }
393
+
394
+ async getFile(fileId) {
395
+ return this.request("getFile", { file_id: fileId })
396
+ }
397
+
398
+ async downloadFile(fileId, destination) {
399
+ const file = await this.getFile(fileId)
400
+ const fileUrl = `https://api.telegram.org/file/bot${this.token}/${file.file_path}`
401
+
402
+ return new Promise((resolve, reject) => {
403
+ https
404
+ .get(fileUrl, (response) => {
405
+ const writeStream = createWriteStream(destination)
406
+ response.pipe(writeStream)
407
+
408
+ writeStream.on("finish", () => {
409
+ writeStream.close()
410
+ resolve(destination)
411
+ })
412
+
413
+ writeStream.on("error", reject)
414
+ })
415
+ .on("error", reject)
416
+ })
417
+ }
418
+
419
+ use(middleware) {
420
+ if (typeof middleware !== "function") {
421
+ throw new Error("Middleware must be a function")
422
+ }
423
+ this.middleware.push(middleware)
424
+ return this
425
+ }
426
+
427
+ command(cmd, handler) {
428
+ if (typeof handler !== "function") {
429
+ throw new Error("Command handler must be a function")
430
+ }
431
+
432
+ const commands = Array.isArray(cmd) ? cmd : [cmd]
433
+ commands.forEach((c) => {
434
+ this.commands.set(c.startsWith("/") ? c : `/${c}`, handler)
435
+ })
436
+
437
+ return this
438
+ }
439
+
440
+ on(event, listener) {
441
+ super.on(event, listener)
442
+ return this
443
+ }
444
+
445
+ async startPolling() {
446
+ if (this.pollingActive) {
447
+ return
448
+ }
449
+
450
+ this.pollingActive = true
451
+
452
+ try {
453
+ await this.deleteWebhook()
454
+ } catch (error) {
455
+ console.error("Failed to delete webhook:", error.message)
456
+ }
457
+
458
+ this.emit("polling_start")
459
+ this._poll()
460
+ }
461
+
462
+ async _poll() {
463
+ if (!this.pollingActive) return
464
+
465
+ try {
466
+ const updates = await this.request("getUpdates", {
467
+ offset: this.offset,
468
+ timeout: this.options.pollingTimeout,
469
+ allowed_updates: this.options.allowedUpdates,
470
+ })
471
+
472
+ for (const update of updates) {
473
+ this.offset = update.update_id + 1
474
+ this._processUpdate(update)
475
+ }
476
+
477
+ setTimeout(() => this._poll(), this.options.pollingInterval)
478
+ } catch (error) {
479
+ this.emit("polling_error", error)
480
+ setTimeout(() => this._poll(), this.options.pollingInterval * 2)
481
+ }
482
+ }
483
+
484
+ stopPolling() {
485
+ this.pollingActive = false
486
+ this.emit("polling_stop")
487
+ }
488
+
489
+ async startWebhook() {
490
+ if (this.webhookServer) {
491
+ return
492
+ }
493
+
494
+ const server = http.createServer((req, res) => {
495
+ if (req.url === this.options.webhookPath && req.method === "POST") {
496
+ let body = ""
497
+
498
+ req.on("data", (chunk) => {
499
+ body += chunk.toString()
500
+ })
501
+
502
+ req.on("end", () => {
503
+ try {
504
+ const update = JSON.parse(body)
505
+ this._processUpdate(update)
506
+ res.writeHead(200)
507
+ res.end("OK")
508
+ } catch (error) {
509
+ this.emit("webhook_error", error)
510
+ res.writeHead(500)
511
+ res.end("Error")
512
+ }
513
+ })
514
+ } else {
515
+ res.writeHead(404)
516
+ res.end("Not Found")
517
+ }
518
+ })
519
+
520
+ server.listen(this.options.webhookPort, () => {
521
+ this.emit("webhook_start", this.options.webhookPort)
522
+ })
523
+
524
+ this.webhookServer = server
525
+ }
526
+
527
+ stopWebhook() {
528
+ if (this.webhookServer) {
529
+ this.webhookServer.close(() => {
530
+ this.emit("webhook_stop")
531
+ })
532
+ this.webhookServer = null
533
+ }
534
+ }
535
+
536
+ async _processUpdate(update) {
537
+ try {
538
+ const ctx = this._createContext(update)
539
+
540
+ await this._runMiddleware(ctx)
541
+
542
+ if (ctx.message?.text?.startsWith("/")) {
543
+ const command = ctx.message.text.split(" ")[0]
544
+ const handler = this.commands.get(command)
545
+
546
+ if (handler) {
547
+ await handler(ctx)
548
+ }
549
+ }
550
+
551
+ this.emit("update", update)
552
+
553
+ if (update.message) {
554
+ this.emit("message", update.message, ctx)
555
+
556
+ if (update.message.text) {
557
+ this.emit("text", update.message, ctx)
558
+ }
559
+
560
+ if (update.message.photo) {
561
+ this.emit("photo", update.message, ctx)
562
+ }
563
+
564
+ if (update.message.document) {
565
+ this.emit("document", update.message, ctx)
566
+ }
567
+
568
+ if (update.message.video) {
569
+ this.emit("video", update.message, ctx)
570
+ }
571
+
572
+ if (update.message.audio) {
573
+ this.emit("audio", update.message, ctx)
574
+ }
575
+
576
+ if (update.message.voice) {
577
+ this.emit("voice", update.message, ctx)
578
+ }
579
+
580
+ if (update.message.sticker) {
581
+ this.emit("sticker", update.message, ctx)
582
+ }
583
+
584
+ if (update.message.location) {
585
+ this.emit("location", update.message, ctx)
586
+ }
587
+
588
+ if (update.message.contact) {
589
+ this.emit("contact", update.message, ctx)
590
+ }
591
+ }
592
+
593
+ if (update.edited_message) {
594
+ this.emit("edited_message", update.edited_message, ctx)
595
+ }
596
+
597
+ if (update.channel_post) {
598
+ this.emit("channel_post", update.channel_post, ctx)
599
+ }
600
+
601
+ if (update.edited_channel_post) {
602
+ this.emit("edited_channel_post", update.edited_channel_post, ctx)
603
+ }
604
+
605
+ if (update.callback_query) {
606
+ this.emit("callback_query", update.callback_query, ctx)
607
+ }
608
+
609
+ if (update.inline_query) {
610
+ this.emit("inline_query", update.inline_query, ctx)
611
+ }
612
+
613
+ if (update.chosen_inline_result) {
614
+ this.emit("chosen_inline_result", update.chosen_inline_result, ctx)
615
+ }
616
+
617
+ if (update.poll) {
618
+ this.emit("poll", update.poll, ctx)
619
+ }
620
+
621
+ if (update.poll_answer) {
622
+ this.emit("poll_answer", update.poll_answer, ctx)
623
+ }
624
+
625
+ if (update.my_chat_member) {
626
+ this.emit("my_chat_member", update.my_chat_member, ctx)
627
+ }
628
+
629
+ if (update.chat_member) {
630
+ this.emit("chat_member", update.chat_member, ctx)
631
+ }
632
+ } catch (error) {
633
+ this.emit("error", error)
634
+ }
635
+ }
636
+
637
+ _createContext(update) {
638
+ const message = update.message || update.edited_message || update.channel_post || update.callback_query?.message
639
+ const chat = message?.chat || update.my_chat_member?.chat || update.chat_member?.chat
640
+ const from = message?.from || update.callback_query?.from || update.inline_query?.from
641
+
642
+ const ctx = {
643
+ update,
644
+ bot: this,
645
+ message,
646
+ chat,
647
+ from,
648
+ callbackQuery: update.callback_query,
649
+ inlineQuery: update.inline_query,
650
+ chosenInlineResult: update.chosen_inline_result,
651
+ poll: update.poll,
652
+ pollAnswer: update.poll_answer,
653
+ myChatMember: update.my_chat_member,
654
+ chatMember: update.chat_member,
655
+ }
656
+
657
+ // Baileys-style simplified response
658
+ ctx.send = (content, opts) => {
659
+ if (!ctx.chat?.id) {
660
+ throw new Error("[Teh] Cannot send message: chat_id is not available in this context")
661
+ }
662
+ return this.sendMessage(ctx.chat.id, content, opts)
663
+ }
664
+
665
+ ctx.reply = (text, opts) => {
666
+ if (!ctx.chat?.id) {
667
+ throw new Error("[Teh] Cannot reply: chat_id is not available in this context")
668
+ }
669
+ return this.sendMessage(ctx.chat.id, text, {
670
+ reply_to_message_id: ctx.message?.message_id,
671
+ ...opts,
672
+ })
673
+ }
674
+
675
+ // Context helpers for media
676
+ ctx.replyWithPhoto = (photo, opts) => ctx.send({ image: photo, ...opts })
677
+ ctx.replyWithVideo = (video, opts) => ctx.send({ video, ...opts })
678
+ ctx.replyWithAudio = (audio, opts) => ctx.send({ audio, ...opts })
679
+ ctx.replyWithDocument = (doc, opts) => ctx.send({ document: doc, ...opts })
680
+
681
+ // Helper methods for callback queries
682
+ ctx.answerCallbackQuery = (options = {}) => {
683
+ if (!ctx.callbackQuery?.id) return Promise.resolve(false)
684
+ return this.answerCallbackQuery(ctx.callbackQuery.id, options)
685
+ }
686
+
687
+ ctx.editMessageText = (text, options = {}) => {
688
+ if (!ctx.callbackQuery?.message) {
689
+ // If it's an inline message (no message object), we need the inline_message_id
690
+ if (ctx.callbackQuery?.inline_message_id) {
691
+ return this.editMessageText(text, {
692
+ inline_message_id: ctx.callbackQuery.inline_message_id,
693
+ ...options,
694
+ })
695
+ }
696
+ return Promise.resolve(false)
697
+ }
698
+ return this.editMessageText(text, {
699
+ chat_id: ctx.chat?.id,
700
+ message_id: ctx.callbackQuery.message.message_id,
701
+ ...options,
702
+ })
703
+ }
704
+
705
+ return ctx
706
+ }
707
+
708
+ async _runMiddleware(ctx, index = 0) {
709
+ if (index >= this.middleware.length) {
710
+ return
711
+ }
712
+
713
+ const middleware = this.middleware[index]
714
+ let nextCalled = false
715
+
716
+ const next = async () => {
717
+ if (nextCalled) {
718
+ throw new Error("next() called multiple times")
719
+ }
720
+ nextCalled = true
721
+ await this._runMiddleware(ctx, index + 1)
722
+ }
723
+
724
+ await middleware(ctx, next)
725
+ }
726
+
727
+ _buildMultipartStream(formData, boundary) {
728
+ const stream = new Readable({ read() {} })
729
+ const nl = "\r\n"
730
+ ;(async () => {
731
+ for (const [key, value] of Object.entries(formData)) {
732
+ if (value === undefined || value === null) continue
733
+
734
+ stream.push(`--${boundary}${nl}`)
735
+ if (value && typeof value === "object" && value.data) {
736
+ const filename = value.filename || `file_${Date.now()}`
737
+ stream.push(`Content-Disposition: form-data; name="${key}"; filename="${filename}"${nl}`)
738
+ stream.push(`Content-Type: ${value.contentType || "application/octet-stream"}${nl}${nl}`)
739
+
740
+ if (value.data instanceof Stream) {
741
+ for await (const chunk of value.data) stream.push(chunk)
742
+ } else {
743
+ stream.push(value.data)
744
+ }
745
+ } else {
746
+ stream.push(`Content-Disposition: form-data; name="${key}"${nl}${nl}`)
747
+ stream.push(typeof value === "object" ? JSON.stringify(value) : String(value))
748
+ }
749
+ stream.push(nl)
750
+ }
751
+ stream.push(`--${boundary}--${nl}`)
752
+ stream.push(null)
753
+ })()
754
+
755
+ return stream
756
+ }
757
+
758
+ async _prepareFile(source, type) {
759
+ if (source instanceof Stream) return { data: source, contentType: this._getMime(type) }
760
+ if (Buffer.isBuffer(source)) return { data: source, contentType: this._getMime(type) }
761
+ if (typeof source === "string") {
762
+ return {
763
+ data: createReadStream(source),
764
+ filename: basename(source),
765
+ contentType: this._getMime(extname(source)),
766
+ }
767
+ }
768
+ return source
769
+ }
770
+
771
+ _getMime(ext) {
772
+ const mimes = {
773
+ photo: "image/jpeg",
774
+ video: "video/mp4",
775
+ audio: "audio/mpeg",
776
+ ".jpg": "image/jpeg",
777
+ ".png": "image/png",
778
+ ".mp4": "video/mp4",
779
+ }
780
+ return mimes[ext] || "application/octet-stream"
781
+ }
782
+
783
+ _formatError(resp) {
784
+ const err = new Error(resp.description || "Unknown Telegram Error")
785
+ err.code = resp.error_code
786
+ err.parameters = resp.parameters
787
+ err.response = resp
788
+ err.name = "TelegramAPIError"
789
+ return err
790
+ }
791
+
792
+ async deleteWebhook(options = {}) {
793
+ return this.request("deleteWebhook", options)
794
+ }
795
+
796
+ async setWebhook(url, options = {}) {
797
+ return this.request("setWebhook", {
798
+ url,
799
+ ...options,
800
+ })
801
+ }
802
+
803
+ async getWebhookInfo() {
804
+ return this.request("getWebhookInfo")
805
+ }
806
+ }
807
+
808
+ class InlineKeyboardBuilder {
809
+ constructor() {
810
+ this.keyboard = []
811
+ this.currentRow = []
812
+ }
813
+
814
+ text(text, callbackData) {
815
+ this.currentRow.push({
816
+ text,
817
+ callback_data: callbackData,
818
+ })
819
+ return this
820
+ }
821
+
822
+ url(text, url) {
823
+ this.currentRow.push({
824
+ text,
825
+ url,
826
+ })
827
+ return this
828
+ }
829
+
830
+ login(text, loginUrl) {
831
+ this.currentRow.push({
832
+ text,
833
+ login_url: loginUrl,
834
+ })
835
+ return this
836
+ }
837
+
838
+ switchInline(text, query = "") {
839
+ this.currentRow.push({
840
+ text,
841
+ switch_inline_query: query,
842
+ })
843
+ return this
844
+ }
845
+
846
+ switchInlineCurrent(text, query = "") {
847
+ this.currentRow.push({
848
+ text,
849
+ switch_inline_query_current_chat: query,
850
+ })
851
+ return this
852
+ }
853
+
854
+ game(text) {
855
+ this.currentRow.push({
856
+ text,
857
+ callback_game: {},
858
+ })
859
+ return this
860
+ }
861
+
862
+ pay(text) {
863
+ this.currentRow.push({
864
+ text,
865
+ pay: true,
866
+ })
867
+ return this
868
+ }
869
+
870
+ row() {
871
+ if (this.currentRow.length > 0) {
872
+ this.keyboard.push([...this.currentRow])
873
+ this.currentRow = []
874
+ }
875
+ return this
876
+ }
877
+
878
+ build() {
879
+ this.row()
880
+ return {
881
+ inline_keyboard: this.keyboard,
882
+ }
883
+ }
884
+ }
885
+
886
+ class ReplyKeyboardBuilder {
887
+ constructor() {
888
+ this.keyboard = []
889
+ this.currentRow = []
890
+ this.options = {}
891
+ }
892
+
893
+ text(text) {
894
+ this.currentRow.push({ text })
895
+ return this
896
+ }
897
+
898
+ requestContact(text) {
899
+ this.currentRow.push({
900
+ text,
901
+ request_contact: true,
902
+ })
903
+ return this
904
+ }
905
+
906
+ requestLocation(text) {
907
+ this.currentRow.push({
908
+ text,
909
+ request_location: true,
910
+ })
911
+ return this
912
+ }
913
+
914
+ requestPoll(text, type) {
915
+ this.currentRow.push({
916
+ text,
917
+ request_poll: { type },
918
+ })
919
+ return this
920
+ }
921
+
922
+ row() {
923
+ if (this.currentRow.length > 0) {
924
+ this.keyboard.push([...this.currentRow])
925
+ this.currentRow = []
926
+ }
927
+ return this
928
+ }
929
+
930
+ resize(resize = true) {
931
+ this.options.resize_keyboard = resize
932
+ return this
933
+ }
934
+
935
+ oneTime(oneTime = true) {
936
+ this.options.one_time_keyboard = oneTime
937
+ return this
938
+ }
939
+
940
+ selective(selective = true) {
941
+ this.options.selective = selective
942
+ return this
943
+ }
944
+
945
+ placeholder(text) {
946
+ this.options.input_field_placeholder = text
947
+ return this
948
+ }
949
+
950
+ build() {
951
+ this.row()
952
+ return {
953
+ keyboard: this.keyboard,
954
+ ...this.options,
955
+ }
956
+ }
957
+ }
958
+
959
+ module.exports = TelegramBot
960
+ module.exports.TelegramBot = TelegramBot
961
+ module.exports.InlineKeyboardBuilder = InlineKeyboardBuilder
962
+ module.exports.ReplyKeyboardBuilder = ReplyKeyboardBuilder