teh-bot 1.0.3 → 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 +105 -35
  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
  }
@@ -716,54 +722,68 @@ class TelegramBot extends EventEmitter {
716
722
  }
717
723
 
718
724
  _buildMultipartStream(formData, boundary) {
719
- const stream = new Readable({ read() {} })
725
+ const stream = new Readable({
726
+ read() {},
727
+ })
720
728
  const nl = "\r\n"
721
729
  ;(async () => {
722
- for (const [key, value] of Object.entries(formData)) {
723
- if (value === undefined || value === null) continue
724
-
725
- stream.push(`--${boundary}${nl}`)
726
- if (value && typeof value === "object" && value.data) {
727
- const filename = value.filename || `file_${Date.now()}`
728
- stream.push(`Content-Disposition: form-data; name="${key}"; filename="${filename}"${nl}`)
729
- stream.push(`Content-Type: ${value.contentType || "application/octet-stream"}${nl}${nl}`)
730
-
731
- if (value.data instanceof Stream) {
732
- 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
+ }
733
753
  } else {
734
- 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))
735
756
  }
736
- } else {
737
- stream.push(`Content-Disposition: form-data; name="${key}"${nl}${nl}`)
738
- stream.push(typeof value === "object" ? JSON.stringify(value) : String(value))
757
+ stream.push(nl)
739
758
  }
740
- 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)
741
764
  }
742
- stream.push(`--${boundary}--${nl}`)
743
- stream.push(null)
744
765
  })()
745
766
 
746
767
  return stream
747
768
  }
748
769
 
749
770
  async _prepareFile(source, type) {
750
- if (source instanceof Stream) return { data: source, contentType: this._getMime(type) }
751
- 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 }
752
775
 
753
776
  if (typeof source === "string") {
754
777
  if (source.startsWith("http")) {
755
- // Fetch remote URL and return as stream
756
778
  return new Promise((resolve, reject) => {
757
- const client = source.startsWith("https") ? https : http
758
- client
779
+ https
759
780
  .get(source, (res) => {
760
- if (res.statusCode !== 200) {
761
- return reject(new Error(`Failed to fetch remote file: ${res.statusCode}`))
762
- }
781
+ const filename = basename(new URL(source).pathname) || `file_${Date.now()}.jpg`
782
+ const contentType = res.headers["content-type"] || defaultMime
763
783
  resolve({
764
784
  data: res,
765
- filename: basename(new URL(source).pathname) || `file_${Date.now()}`,
766
- contentType: res.headers["content-type"] || this._getMime(type),
785
+ filename: filename,
786
+ contentType: contentType,
767
787
  })
768
788
  })
769
789
  .on("error", reject)
@@ -771,25 +791,34 @@ class TelegramBot extends EventEmitter {
771
791
  }
772
792
 
773
793
  // Local file
794
+ const filename = basename(source)
774
795
  return {
775
796
  data: createReadStream(source),
776
- filename: basename(source),
777
- contentType: this._getMime(extname(source)),
797
+ filename: filename,
798
+ contentType: this._getMime(extname(filename)) || defaultMime,
778
799
  }
779
800
  }
780
801
  return source
781
802
  }
782
803
 
783
- _getMime(ext) {
804
+ _getMime(extOrType) {
784
805
  const mimes = {
785
806
  photo: "image/jpeg",
807
+ image: "image/jpeg",
786
808
  video: "video/mp4",
787
809
  audio: "audio/mpeg",
810
+ document: "application/octet-stream",
788
811
  ".jpg": "image/jpeg",
812
+ ".jpeg": "image/jpeg",
789
813
  ".png": "image/png",
814
+ ".gif": "image/gif",
790
815
  ".mp4": "video/mp4",
816
+ ".mp3": "audio/mpeg",
817
+ ".pdf": "application/pdf",
818
+ ".zip": "application/zip",
791
819
  }
792
- return mimes[ext] || "application/octet-stream"
820
+ const key = extOrType?.toLowerCase()
821
+ return mimes[key] || (key?.startsWith(".") ? "application/octet-stream" : mimes.photo)
793
822
  }
794
823
 
795
824
  _flattenOptions(options) {
@@ -828,6 +857,47 @@ class TelegramBot extends EventEmitter {
828
857
  return this.request("getWebhookInfo")
829
858
  }
830
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
+
831
901
  static InlineKeyboard() {
832
902
  return new InlineKeyboardBuilder()
833
903
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teh-bot",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Lightweight, high-performance Telegram Bot API module with zero dependencies",
5
5
  "keywords": [
6
6
  "telegram",