teh-bot 1.0.0 → 1.0.2

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 +80 -50
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -19,10 +19,6 @@ const DEFAULT_OPTIONS = {
19
19
  baseApiUrl: "https://api.telegram.org",
20
20
  }
21
21
 
22
- /**
23
- * Modern, High-Performance Telegram Bot API Module
24
- * Inspired by Baileys simplified API and Telegraf's robust system
25
- */
26
22
  class TelegramBot extends EventEmitter {
27
23
  constructor(token, options = {}) {
28
24
  super()
@@ -121,50 +117,13 @@ class TelegramBot extends EventEmitter {
121
117
  if (!mediaType) return this.request("sendMessage", { chat_id: chatId, text: content.text, ...options })
122
118
 
123
119
  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
- }
120
+ const formData = { chat_id: chatId, caption, [mediaType]: await this._prepareFile(mediaSource, mediaType), ...rest }
134
121
 
135
- formData[mediaType] = mediaSource
136
- return this.request(method, formData)
122
+ return this.request(method, {}, formData)
137
123
  }
138
124
 
139
125
  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
- }
126
+ return this._sendFile("sendPhoto", chatId, photo, "photo", options)
168
127
  }
169
128
 
170
129
  async sendAudio(chatId, audio, options = {}) {
@@ -636,8 +595,25 @@ class TelegramBot extends EventEmitter {
636
595
 
637
596
  _createContext(update) {
638
597
  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
598
+
599
+ // 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
+ }
607
+
608
+ const from =
609
+ message?.from ||
610
+ update.callback_query?.from ||
611
+ update.inline_query?.from ||
612
+ update.chosen_inline_result?.from ||
613
+ update.shipping_query?.from ||
614
+ update.pre_checkout_query?.from ||
615
+ update.poll_answer?.user ||
616
+ update.my_chat_member?.from
641
617
 
642
618
  const ctx = {
643
619
  update,
@@ -656,17 +632,20 @@ class TelegramBot extends EventEmitter {
656
632
 
657
633
  // Baileys-style simplified response
658
634
  ctx.send = (content, opts) => {
659
- if (!ctx.chat?.id) {
635
+ const chatId = ctx.chat?.id || update.callback_query?.from?.id || update.inline_query?.from?.id
636
+ if (!chatId) {
637
+ console.error("[v0] Context update without chat_id:", JSON.stringify(update))
660
638
  throw new Error("[Teh] Cannot send message: chat_id is not available in this context")
661
639
  }
662
- return this.sendMessage(ctx.chat.id, content, opts)
640
+ return this.sendMessage(chatId, content, opts)
663
641
  }
664
642
 
665
643
  ctx.reply = (text, opts) => {
666
- if (!ctx.chat?.id) {
644
+ const chatId = ctx.chat?.id || update.callback_query?.from?.id
645
+ if (!chatId) {
667
646
  throw new Error("[Teh] Cannot reply: chat_id is not available in this context")
668
647
  }
669
- return this.sendMessage(ctx.chat.id, text, {
648
+ return this.sendMessage(chatId, text, {
670
649
  reply_to_message_id: ctx.message?.message_id,
671
650
  ...opts,
672
651
  })
@@ -758,7 +737,28 @@ class TelegramBot extends EventEmitter {
758
737
  async _prepareFile(source, type) {
759
738
  if (source instanceof Stream) return { data: source, contentType: this._getMime(type) }
760
739
  if (Buffer.isBuffer(source)) return { data: source, contentType: this._getMime(type) }
740
+
761
741
  if (typeof source === "string") {
742
+ if (source.startsWith("http")) {
743
+ // Fetch remote URL and return as stream
744
+ return new Promise((resolve, reject) => {
745
+ const client = source.startsWith("https") ? https : http
746
+ client
747
+ .get(source, (res) => {
748
+ if (res.statusCode !== 200) {
749
+ return reject(new Error(`Failed to fetch remote file: ${res.statusCode}`))
750
+ }
751
+ resolve({
752
+ data: res,
753
+ filename: basename(new URL(source).pathname) || `file_${Date.now()}`,
754
+ contentType: res.headers["content-type"] || this._getMime(type),
755
+ })
756
+ })
757
+ .on("error", reject)
758
+ })
759
+ }
760
+
761
+ // Local file
762
762
  return {
763
763
  data: createReadStream(source),
764
764
  filename: basename(source),
@@ -780,6 +780,18 @@ class TelegramBot extends EventEmitter {
780
780
  return mimes[ext] || "application/octet-stream"
781
781
  }
782
782
 
783
+ _flattenOptions(options) {
784
+ const flat = {}
785
+ for (const [key, value] of Object.entries(options)) {
786
+ if (typeof value === "object" && value !== null) {
787
+ flat[key] = JSON.stringify(value)
788
+ } else {
789
+ flat[key] = value
790
+ }
791
+ }
792
+ return flat
793
+ }
794
+
783
795
  _formatError(resp) {
784
796
  const err = new Error(resp.description || "Unknown Telegram Error")
785
797
  err.code = resp.error_code
@@ -803,6 +815,24 @@ class TelegramBot extends EventEmitter {
803
815
  async getWebhookInfo() {
804
816
  return this.request("getWebhookInfo")
805
817
  }
818
+
819
+ static InlineKeyboard() {
820
+ return new InlineKeyboardBuilder()
821
+ }
822
+
823
+ static ReplyKeyboard() {
824
+ return new ReplyKeyboardBuilder()
825
+ }
826
+
827
+ static RemoveKeyboard(selective = false) {
828
+ return { remove_keyboard: true, selective }
829
+ }
830
+
831
+ static ForceReply(selective = false, placeholder = "") {
832
+ const obj = { force_reply: true, selective }
833
+ if (placeholder) obj.input_field_placeholder = placeholder
834
+ return obj
835
+ }
806
836
  }
807
837
 
808
838
  class InlineKeyboardBuilder {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teh-bot",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Lightweight, high-performance Telegram Bot API module with zero dependencies",
5
5
  "keywords": [
6
6
  "telegram",