teh-bot 1.0.2 → 1.0.4

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 (2) hide show
  1. package/index.js +130 -48
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -86,8 +86,14 @@ class TelegramBot extends EventEmitter {
86
86
  })
87
87
 
88
88
  req.on("error", reject)
89
- if (body instanceof Stream) body.pipe(req)
90
- else {
89
+ if (body instanceof Stream) {
90
+ body.pipe(req)
91
+ body.on("end", () => req.end())
92
+ body.on("error", (err) => {
93
+ req.destroy()
94
+ reject(err)
95
+ })
96
+ } else {
91
97
  req.write(body)
92
98
  req.end()
93
99
  }
@@ -384,6 +390,11 @@ class TelegramBot extends EventEmitter {
384
390
  }
385
391
 
386
392
  command(cmd, handler) {
393
+ if (handler === undefined) {
394
+ const commandName = typeof cmd === "string" ? (cmd.startsWith("/") ? cmd : `/${cmd}`) : ""
395
+ return this.commands.get(commandName)
396
+ }
397
+
387
398
  if (typeof handler !== "function") {
388
399
  throw new Error("Command handler must be a function")
389
400
  }
@@ -597,16 +608,17 @@ class TelegramBot extends EventEmitter {
597
608
  const message = update.message || update.edited_message || update.channel_post || update.callback_query?.message
598
609
 
599
610
  // Fallback logic for chat extraction from various update types
600
- let chat =
601
- message?.chat || update.my_chat_member?.chat || update.chat_member?.chat || update.chat_join_request?.chat
602
-
603
- // Explicit extraction for callback_query which might not have a message (inline buttons)
604
- if (!chat && update.callback_query) {
605
- chat = update.callback_query.message?.chat
606
- }
611
+ const chat =
612
+ update.message?.chat ||
613
+ update.callback_query?.message?.chat ||
614
+ update.my_chat_member?.chat ||
615
+ update.chat_member?.chat ||
616
+ update.chat_join_request?.chat ||
617
+ update.edited_message?.chat ||
618
+ update.channel_post?.chat
607
619
 
608
620
  const from =
609
- message?.from ||
621
+ update.message?.from ||
610
622
  update.callback_query?.from ||
611
623
  update.inline_query?.from ||
612
624
  update.chosen_inline_result?.from ||
@@ -632,18 +644,24 @@ class TelegramBot extends EventEmitter {
632
644
 
633
645
  // Baileys-style simplified response
634
646
  ctx.send = (content, opts) => {
635
- const chatId = ctx.chat?.id || update.callback_query?.from?.id || update.inline_query?.from?.id
647
+ const chatId =
648
+ ctx.chat?.id ||
649
+ update.callback_query?.from?.id ||
650
+ update.inline_query?.from?.id ||
651
+ update.message?.from?.id ||
652
+ from?.id
653
+
636
654
  if (!chatId) {
637
- console.error("[v0] Context update without chat_id:", JSON.stringify(update))
638
- throw new Error("[Teh] Cannot send message: chat_id is not available in this context")
655
+ console.error("[v0] Context update without valid chatId destination:", JSON.stringify(update))
656
+ throw new Error("[Teh] Cannot send message: chat_id could not be resolved from this update context")
639
657
  }
640
658
  return this.sendMessage(chatId, content, opts)
641
659
  }
642
660
 
643
661
  ctx.reply = (text, opts) => {
644
- const chatId = ctx.chat?.id || update.callback_query?.from?.id
662
+ const chatId = ctx.chat?.id || update.callback_query?.from?.id || from?.id
645
663
  if (!chatId) {
646
- throw new Error("[Teh] Cannot reply: chat_id is not available in this context")
664
+ throw new Error("[Teh] Cannot reply: chat_id could not be resolved from this update context")
647
665
  }
648
666
  return this.sendMessage(chatId, text, {
649
667
  reply_to_message_id: ctx.message?.message_id,
@@ -704,54 +722,68 @@ class TelegramBot extends EventEmitter {
704
722
  }
705
723
 
706
724
  _buildMultipartStream(formData, boundary) {
707
- const stream = new Readable({ read() {} })
725
+ const stream = new Readable({
726
+ read() {},
727
+ })
708
728
  const nl = "\r\n"
709
729
  ;(async () => {
710
- for (const [key, value] of Object.entries(formData)) {
711
- if (value === undefined || value === null) continue
712
-
713
- stream.push(`--${boundary}${nl}`)
714
- if (value && typeof value === "object" && value.data) {
715
- const filename = value.filename || `file_${Date.now()}`
716
- stream.push(`Content-Disposition: form-data; name="${key}"; filename="${filename}"${nl}`)
717
- stream.push(`Content-Type: ${value.contentType || "application/octet-stream"}${nl}${nl}`)
718
-
719
- if (value.data instanceof Stream) {
720
- for await (const chunk of value.data) stream.push(chunk)
730
+ try {
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 || value instanceof Stream || Buffer.isBuffer(value))) {
736
+ const fileData = value.data || value
737
+ const filename = value.filename || `file_${Date.now()}.jpg`
738
+ const contentType = value.contentType || this._getMime(extname(filename)) || "image/jpeg"
739
+
740
+ stream.push(`Content-Disposition: form-data; name="${key}"; filename="${filename}"${nl}`)
741
+ stream.push(`Content-Type: ${contentType}${nl}${nl}`)
742
+
743
+ if (fileData instanceof Stream) {
744
+ for await (const chunk of fileData) {
745
+ const canPush = stream.push(chunk)
746
+ if (!canPush) {
747
+ await new Promise((resolve) => stream.once("drain", resolve))
748
+ }
749
+ }
750
+ } else {
751
+ stream.push(fileData)
752
+ }
721
753
  } else {
722
- stream.push(value.data)
754
+ stream.push(`Content-Disposition: form-data; name="${key}"${nl}${nl}`)
755
+ stream.push(typeof value === "object" ? JSON.stringify(value) : String(value))
723
756
  }
724
- } else {
725
- stream.push(`Content-Disposition: form-data; name="${key}"${nl}${nl}`)
726
- stream.push(typeof value === "object" ? JSON.stringify(value) : String(value))
757
+ stream.push(nl)
727
758
  }
728
- stream.push(nl)
759
+ stream.push(`--${boundary}--${nl}`)
760
+ } catch (err) {
761
+ console.error("[v0] Error building multipart stream:", err)
762
+ } finally {
763
+ stream.push(null)
729
764
  }
730
- stream.push(`--${boundary}--${nl}`)
731
- stream.push(null)
732
765
  })()
733
766
 
734
767
  return stream
735
768
  }
736
769
 
737
770
  async _prepareFile(source, type) {
738
- if (source instanceof Stream) return { data: source, contentType: this._getMime(type) }
739
- if (Buffer.isBuffer(source)) return { data: source, contentType: this._getMime(type) }
771
+ const defaultMime = this._getMime(type) || "image/jpeg"
772
+
773
+ if (source instanceof Stream) return { data: source, contentType: defaultMime }
774
+ if (Buffer.isBuffer(source)) return { data: source, contentType: defaultMime }
740
775
 
741
776
  if (typeof source === "string") {
742
777
  if (source.startsWith("http")) {
743
- // Fetch remote URL and return as stream
744
778
  return new Promise((resolve, reject) => {
745
- const client = source.startsWith("https") ? https : http
746
- client
779
+ https
747
780
  .get(source, (res) => {
748
- if (res.statusCode !== 200) {
749
- return reject(new Error(`Failed to fetch remote file: ${res.statusCode}`))
750
- }
781
+ const filename = basename(new URL(source).pathname) || `file_${Date.now()}.jpg`
782
+ const contentType = res.headers["content-type"] || defaultMime
751
783
  resolve({
752
784
  data: res,
753
- filename: basename(new URL(source).pathname) || `file_${Date.now()}`,
754
- contentType: res.headers["content-type"] || this._getMime(type),
785
+ filename: filename,
786
+ contentType: contentType,
755
787
  })
756
788
  })
757
789
  .on("error", reject)
@@ -759,25 +791,34 @@ class TelegramBot extends EventEmitter {
759
791
  }
760
792
 
761
793
  // Local file
794
+ const filename = basename(source)
762
795
  return {
763
796
  data: createReadStream(source),
764
- filename: basename(source),
765
- contentType: this._getMime(extname(source)),
797
+ filename: filename,
798
+ contentType: this._getMime(extname(filename)) || defaultMime,
766
799
  }
767
800
  }
768
801
  return source
769
802
  }
770
803
 
771
- _getMime(ext) {
804
+ _getMime(extOrType) {
772
805
  const mimes = {
773
806
  photo: "image/jpeg",
807
+ image: "image/jpeg",
774
808
  video: "video/mp4",
775
809
  audio: "audio/mpeg",
810
+ document: "application/octet-stream",
776
811
  ".jpg": "image/jpeg",
812
+ ".jpeg": "image/jpeg",
777
813
  ".png": "image/png",
814
+ ".gif": "image/gif",
778
815
  ".mp4": "video/mp4",
816
+ ".mp3": "audio/mpeg",
817
+ ".pdf": "application/pdf",
818
+ ".zip": "application/zip",
779
819
  }
780
- return mimes[ext] || "application/octet-stream"
820
+ const key = extOrType?.toLowerCase()
821
+ return mimes[key] || (key?.startsWith(".") ? "application/octet-stream" : mimes.photo)
781
822
  }
782
823
 
783
824
  _flattenOptions(options) {
@@ -816,6 +857,47 @@ class TelegramBot extends EventEmitter {
816
857
  return this.request("getWebhookInfo")
817
858
  }
818
859
 
860
+ async getMe() {
861
+ return this.request("getMe")
862
+ }
863
+
864
+ async _sendFile(method, chatId, source, type, options = {}) {
865
+ const formData = {
866
+ chat_id: chatId,
867
+ [type]: await this._prepareFile(source, type),
868
+ ...this._flattenOptions(options),
869
+ }
870
+ return this.request(method, {}, formData)
871
+ }
872
+
873
+ async setMyCommands(commands, options = {}) {
874
+ return this.request("setMyCommands", { commands, ...options })
875
+ }
876
+
877
+ async getMyCommands(options = {}) {
878
+ return this.request("getMyCommands", options)
879
+ }
880
+
881
+ async setChatMenuButton(options = {}) {
882
+ return this.request("setChatMenuButton", options)
883
+ }
884
+
885
+ async getChatMenuButton(options = {}) {
886
+ return this.request("getChatMenuButton", options)
887
+ }
888
+
889
+ async getUserProfilePhotos(userId, options = {}) {
890
+ return this.request("getUserProfilePhotos", { user_id: userId, ...options })
891
+ }
892
+
893
+ async createChatInviteLink(chatId, options = {}) {
894
+ return this.request("createChatInviteLink", { chat_id: chatId, ...options })
895
+ }
896
+
897
+ async revokeChatInviteLink(chatId, inviteLink) {
898
+ return this.request("revokeChatInviteLink", { chat_id: chatId, invite_link: inviteLink })
899
+ }
900
+
819
901
  static InlineKeyboard() {
820
902
  return new InlineKeyboardBuilder()
821
903
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teh-bot",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Lightweight, high-performance Telegram Bot API module with zero dependencies",
5
5
  "keywords": [
6
6
  "telegram",