UNPKG

bale-bot-ts

Version:

A TypeScript SDK for Bale.ai bots

1,007 lines (902 loc) 37.6 kB
import axios, { AxiosInstance, AxiosResponse } from 'axios'; import FormData from 'form-data'; // Import FormData for Node.js multipart/form-data import EventEmitter from 'events'; // --- Core API Interfaces --- interface BotResponse<T> { ok: boolean; result: T; description?: string; error_code?: number; parameters?: object; // ResponseParameters } // --- انواع اصلی (Entities) --- interface User { id: number; is_bot: boolean; first_name: string; last_name?: string; username?: string; language_code?: string; } interface Chat { id: number; type: 'private' | 'group' | 'channel'; title?: string; username?: string; first_name?: string; last_name?: string; photo?: ChatPhoto; } interface Message { message_id: number; from?: User; date: number; chat: Chat; forward_from?: User; forward_from_chat?: Chat; forward_from_message_id?: number; forward_date?: number; reply_to_message?: Message; edit_date?: number; text?: string; animation?: Animation; audio?: Audio; document?: Document; photo?: PhotoSize[]; sticker?: Sticker; video?: Video; voice?: Voice; caption?: string; contact?: Contact; location?: Location; new_chat_members?: User[]; left_chat_member?: User; invoice?: Invoice; successful_payment?: SuccessfulPayment; web_app_data?: WebAppData; reply_markup?: InlineKeyboardMarkup | ReplyKeyboardMarkup; } interface MessageId { message_id: number; } interface PhotoSize { file_id: string; file_unique_id: string; width: number; height: number; file_size?: number; } interface Animation { file_id: string; file_unique_id: string; width: number; height: number; duration: number; thumbnail?: PhotoSize; file_name?: string; mime_type?: string; file_size?: number; } interface Audio { file_id: string; file_unique_id: string; duration: number; title?: string; file_name?: string; mime_type?: string; file_size?: number; } interface Document { file_id: string; file_unique_id: string; thumbnail?: PhotoSize; file_name?: string; mime_type?: string; file_size?: number; } interface Video { file_id: string; file_unique_id: string; width: number; height: number; duration: number; file_name?: string; mime_type?: string; file_size?: number; } interface Voice { file_id: string; file_unique_id: string; duration?: number; } interface Contact { phone_number: string; first_name: string; last_name?: string; user_id?: number; } interface Location { longitude: number; latitude: number; } interface File { file_id: string; file_unique_id: string; file_size?: number; file_path?: string; } interface ReplyKeyboardMarkup { keyboard: KeyboardButton[][]; } interface KeyboardButton { text: string; request_contact?: boolean; request_location?: boolean; web_app?: WebAppInfo; } // --- انواع پیچیده‌تر (Complex Types) --- interface InlineKeyboardMarkup { inline_keyboard: InlineKeyboardButton[][]; } interface InlineKeyboardButton { text: string; url?: string; callback_data?: string; // 1-64 bytes web_app?: WebAppInfo; copy_text?: CopyTextButton; } interface ReplyKeyboardRemove { remove_keyboard: true; } interface CallbackQuery { id: string; from: User; message?: Message; data?: string; } interface WebAppData { data: string; } interface WebAppInfo { url: string; // HTTPS URL } interface CopyTextButton { text: string; // 1-256 characters } interface ChatPhoto { small_file_id: string; small_file_unique_id: string; big_file_id: string; big_file_unique_id: string; } type InputFile = Buffer | NodeJS.ReadableStream | string; // Represents file content for multipart/form-data or file_id/URL interface InputMediaBase { type: string; // "photo", "video", etc. media: string; // file_id, HTTP URL, or "attach://file_attach_name" caption?: string; // 0-1024 characters } interface InputMediaPhoto extends InputMediaBase { type: 'photo'; } interface InputMediaVideo extends InputMediaBase { type: 'video'; thumbnail?: string; // file_id, HTTP URL, or "attach://file_attach_name" width?: number; height?: number; duration?: number; } interface InputMediaAnimation extends InputMediaBase { type: 'animation'; thumbnail?: string; // file_id, HTTP URL, or "attach://file_attach_name" width?: number; height?: number; duration?: number; } interface InputMediaAudio extends InputMediaBase { type: 'audio'; thumbnail?: string; // file_id, HTTP URL, or "attach://file_attach_name" duration?: number; title?: string; } interface InputMediaDocument extends InputMediaBase { type: 'document'; thumbnail?: string; // file_id, HTTP URL, or "attach://file_attach_name" } type InputMedia = InputMediaAnimation | InputMediaDocument | InputMediaAudio | InputMediaPhoto | InputMediaVideo; interface Sticker { file_id: string; file_unique_id: string; type: 'regular' | 'mask'; width: number; height: number; file_size?: number; } interface StickerSet { name: string; title: string; stickers: Sticker[]; thumbnail?: PhotoSize; } interface InputSticker { sticker: string; // file_id or "attach://file_attach_name" } // --- انواع مربوط به پرداخت (Payment Types) --- interface LabeledPrice { label: string; amount: number; // In Rial (Integer) } // Corrected: Define Invoice interface interface Invoice { title: string; description: string; payload: string; currency: string; // Added based on common API patterns, not explicitly in the snippet you shared for Invoice, but present in PreCheckoutQuery/SuccessfulPayment total_amount: number; // Added based on common API patterns, present in PreCheckoutQuery/SuccessfulPayment } interface PreCheckoutQuery { id: string; from: User; currency: string; // "IRR" total_amount: number; invoice_payload: string; shipping_option_id?: string; order_info?: OrderInfo; } interface OrderInfo { name?: string; phone_number?: string; email?: string; shipping_address?: ShippingAddress; } interface ShippingAddress { country_code: string; state: string; city: string; street_line1: string; street_line2: string; post_code: string; } interface SuccessfulPayment { currency: string; // "IRR" total_amount: number; invoice_payload: string; telegram_payment_charge_id: string; provider_payment_charge_id?: string; } interface Transaction { id: string; status: 'pending' | 'succeed' | 'failed' | 'rejected' | 'timeout'; userID: number; amount: number; createdAt: number; // Unix timestamp } // --- Method-Specific Options Interfaces (Now correctly defined) --- interface SendMessageOptions { chat_id: number | string; text: string; parse_mode?: 'html' | 'markdown'; disable_web_page_preview?: boolean; disable_notification?: boolean; reply_to_message_id?: number; reply_markup?: InlineKeyboardMarkup | ReplyKeyboardMarkup | ReplyKeyboardRemove; } interface GetUpdatesOptions { offset?: number; limit?: number; // 1-100, default 100 timeout?: number; // In seconds for long polling } interface SetWebhookOptions { url: string; // HTTPS URL } interface WebhookInfo { url: string; has_custom_certificate: boolean; pending_update_count: number; ip_address?: string; last_error_date?: number; last_error_message?: string; last_synchronization_error_date?: number; max_connections?: number; allowed_updates?: string[]; } // --- Update Interface (for getUpdates) --- interface Update { update_id: number; message?: Message; edited_message?: Message; channel_post?: Message; edited_channel_post?: Message; callback_query?: CallbackQuery; pre_checkout_query?: PreCheckoutQuery; // Add more fields as needed based on Bale API } // --- Main BaleBotClient Class --- export class BaleBotClient extends EventEmitter { private api: AxiosInstance; private readonly baseURL: string = 'https://tapi.bale.ai/'; // Base URL for Bale Bot API private pollingInterval: ReturnType<typeof setInterval> | null = null; private lastUpdateId: number = 0; /** * Creates a new BaleBotClient instance. * @param token - The unique authentication token for your bot. */ constructor(private token: string) { super(); if (!token) { throw new Error('Bale Bot Token is required.'); } this.api = axios.create({ baseURL: `${this.baseURL}bot${this.token}/`, headers: { 'Content-Type': 'application/json', // Default content type }, }); } private async sendRequest<T>(method: string, data?: Record<string, any>, isFileRequest: boolean = false): Promise<BotResponse<T>> { try { let response: AxiosResponse<BotResponse<T>>; if (isFileRequest && data) { const formData = new FormData(); for (const key in data) { if (Object.prototype.hasOwnProperty.call(data, key)) { // If the value is a file (Buffer or ReadableStream) if (data[key] instanceof Buffer || (data[key] && typeof data[key].pipe === 'function')) { // Append file with a generic filename using the third argument as options for form-data formData.append(key, data[key], { filename: key }); } else if (typeof data[key] === 'object' && data[key] !== null) { // For objects or arrays, stringify them formData.append(key, JSON.stringify(data[key])); } else { // For primitive types formData.append(key, data[key]); } } } response = await this.api.post(method, formData, { headers: { // Ensure form-data generates the correct boundary ...formData.getHeaders() } }); } else { // For non-file requests, default to JSON POST response = await this.api.post(method, data); } if (!response.data.ok) { throw new Error(`Bale API Error: ${response.data.description || 'Unknown error'} (Code: ${response.data.error_code})`); } return response.data; } catch (error: any) { console.error(`Error in ${method}:`, error.response?.data || error.message); throw new Error(`Failed to call ${method}: ${error.response?.data?.description || error.message}`); } } public startPolling(interval: number = 3000) { if (this.pollingInterval) return; // جلوگیری از شروع مجدد this.pollingInterval = setInterval(async () => { try { const response = await this.api.get<BotResponse<Update[]>>(`getUpdates`, { params: { offset: this.lastUpdateId + 1, timeout: 0, }, }); if (response.data.ok && Array.isArray(response.data.result)) { for (const update of response.data.result) { this.lastUpdateId = update.update_id; if (update.message) { this.emit('message', update.message); } } } } catch (error: any) { console.error('[Polling error]', error.message); } }, interval); } public stopPolling() { if (this.pollingInterval) { clearInterval(this.pollingInterval); this.pollingInterval = null; } } // --- متدهای موجود (Available Methods) --- /** * A simple method for testing your bot's authentication token. * @returns Basic information about the bot in the form of a User object. */ public async getMe(): Promise<BotResponse<User>> { return this.sendRequest<User>('getMe'); } /** * Use this method to log out from the cloud Bot API server before launching the bot in a test environment. * @returns True on success. */ public async logout(): Promise<BotResponse<boolean>> { return this.sendRequest<boolean>('logout'); } /** * Use this method to close the bot instance before moving it from a local server or test environment to the production server. * @returns True on success. */ public async close(): Promise<BotResponse<boolean>> { return this.sendRequest<boolean>('close'); } /** * Use this method to send text messages. * @param options - Options for sending the message. * @returns The sent Message object on success. */ public async sendMessage(options: SendMessageOptions): Promise<BotResponse<Message>> { return this.sendRequest<Message>('sendMessage', options); } /** * Use this method to forward messages of any kind. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @param from_chat_id - Unique identifier for the chat where the original message was sent. * @param message_id - Unique message identifier in the original chat. * @returns The forwarded Message on success. */ public async forwardMessage( chat_id: string | number, from_chat_id: string | number, message_id: number ): Promise<BotResponse<Message>> { return this.sendRequest<Message>('forwardMessage', { chat_id, from_chat_id, message_id }); } /** * Use this method to copy messages of any kind. Service messages and invoice messages can't be copied. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @param from_chat_id - Unique identifier for the chat where the original message was sent. * @param message_id - Unique message identifier in the original chat. * @returns The MessageId of the sent message on success. */ public async copyMessage( chat_id: string | number, from_chat_id: string | number, message_id: number ): Promise<BotResponse<MessageId>> { return this.sendRequest<MessageId>('copyMessage', { chat_id, from_chat_id, message_id }); } /** * Use this method to send photos. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @param photo - Photo to send. Pass a file_id, HTTP URL, or upload a new photo using multipart/form-data. * @param caption - Photo caption (0-1024 characters). * @param reply_to_message_id - If the message is a reply, ID of the original message. * @param reply_markup - Additional interface options. * @returns The sent Message on success. */ public async sendPhoto( chat_id: string | number, photo: string | InputFile, caption?: string, reply_to_message_id?: number, reply_markup?: InlineKeyboardMarkup | ReplyKeyboardMarkup | ReplyKeyboardRemove ): Promise<BotResponse<Message>> { const data: Record<string, any> = { chat_id, photo }; if (caption) data.caption = caption; if (reply_to_message_id) data.reply_to_message_id = reply_to_message_id; if (reply_markup) data.reply_markup = reply_markup; return this.sendRequest<Message>('sendPhoto', data, typeof photo !== 'string'); } /** * Use this method to send audio files, if you want Bale clients to display them in the music player. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @param audio - Audio file to send. Pass a file_id, HTTP URL, or upload a new audio file using multipart/form-data. * @param caption - Audio caption (0-1024 characters). * @param reply_to_message_id - If the message is a reply, ID of the original message. * @param reply_markup - Additional interface options. * @returns The sent Message on success. */ public async sendAudio( chat_id: string | number, audio: string | InputFile, caption?: string, reply_to_message_id?: number, reply_markup?: InlineKeyboardMarkup | ReplyKeyboardMarkup | ReplyKeyboardRemove ): Promise<BotResponse<Message>> { const data: Record<string, any> = { chat_id, audio }; if (caption) data.caption = caption; if (reply_to_message_id) data.reply_to_message_id = reply_to_message_id; if (reply_markup) data.reply_markup = reply_markup; return this.sendRequest<Message>('sendAudio', data, typeof audio !== 'string'); } /** * Use this method to send general files. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @param document - File to send. Pass a file_id, HTTP URL, or upload a new file using multipart/form-data. * @param caption - Document caption (0-1024 characters). * @param reply_to_message_id - If the message is a reply, ID of the original message. * @param reply_markup - Additional interface options. * @returns The sent Message on success. */ public async sendDocument( chat_id: string | number, document: string | InputFile, caption?: string, reply_to_message_id?: number, reply_markup?: InlineKeyboardMarkup | ReplyKeyboardMarkup | ReplyKeyboardRemove ): Promise<BotResponse<Message>> { const data: Record<string, any> = { chat_id, document }; if (caption) data.caption = caption; if (reply_to_message_id) data.reply_to_message_id = reply_to_message_id; if (reply_markup) data.reply_markup = reply_markup; return this.sendRequest<Message>('sendDocument', data, typeof document !== 'string'); } /** * Use this method to send video files. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @param video - Video to send. Pass a file_id, HTTP URL, or upload a new video file using multipart/form-data. * @param caption - Video caption (0-1024 characters). * @param reply_to_message_id - If the message is a reply, ID of the original message. * @param reply_markup - Additional interface options. * @returns The sent Message on success. */ public async sendVideo( chat_id: string | number, video: string | InputFile, caption?: string, reply_to_message_id?: number, reply_markup?: InlineKeyboardMarkup | ReplyKeyboardMarkup | ReplyKeyboardRemove ): Promise<BotResponse<Message>> { const data: Record<string, any> = { chat_id, video }; if (caption) data.caption = caption; if (reply_to_message_id) data.reply_to_message_id = reply_to_message_id; if (reply_markup) data.reply_markup = reply_markup; return this.sendRequest<Message>('sendVideo', data, typeof video !== 'string'); } /** * Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). * @param chat_id - Unique identifier for the target chat or username of the target channel. * @param animation - Animation to send. Pass a file_id, HTTP URL, or upload a new animation file using multipart/form-data. * @param reply_to_message_id - If the message is a reply, ID of the original message. * @param reply_markup - Additional interface options. * @returns The sent Message on success. */ public async sendAnimation( chat_id: string | number, animation: string | InputFile, reply_to_message_id?: number, reply_markup?: InlineKeyboardMarkup | ReplyKeyboardMarkup | ReplyKeyboardRemove ): Promise<BotResponse<Message>> { const data: Record<string, any> = { chat_id, animation }; if (reply_to_message_id) data.reply_to_message_id = reply_to_message_id; if (reply_markup) data.reply_markup = reply_markup; return this.sendRequest<Message>('sendAnimation', data, typeof animation !== 'string'); } /** * Use this method to send audio files, if you want Bale clients to display the file as a playable voice message. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @param voice - Audio file to send. Pass a file_id, HTTP URL, or upload a new voice file using multipart/form-data. * @param caption - Voice message caption (0-1024 characters). * @param reply_to_message_id - If the message is a reply, ID of the original message. * @param reply_markup - Additional interface options. * @returns The sent Message on success. */ public async sendVoice( chat_id: string | number, voice: string | InputFile, caption?: string, reply_to_message_id?: number, reply_markup?: InlineKeyboardMarkup | ReplyKeyboardMarkup | ReplyKeyboardRemove ): Promise<BotResponse<Message>> { const data: Record<string, any> = { chat_id, voice }; if (caption) data.caption = caption; if (reply_to_message_id) data.reply_to_message_id = reply_to_message_id; if (reply_markup) data.reply_markup = reply_markup; return this.sendRequest<Message>('sendVoice', data, typeof voice !== 'string'); } /** * Use this method to send a group of photos, videos, documents or audios as an album. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @param media - A JSON-serialized array describing messages to be sent. * @param reply_to_message_id - If the message is a reply, ID of the original message. * @returns An array of sent Messages on success. */ public async sendMediaGroup( chat_id: string | number, media: InputMedia[], reply_to_message_id?: number ): Promise<BotResponse<Message[]>> { const data: Record<string, any> = { chat_id, media: JSON.stringify(media) }; if (reply_to_message_id) data.reply_to_message_id = reply_to_message_id; // Check if any media item requires multipart/form-data const requiresMultipart = media.some(item => item.media.startsWith('attach://')); return this.sendRequest<Message[]>('sendMediaGroup', data, requiresMultipart); } /** * Use this method to send point on the map. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @param latitude - Latitude of the location. * @param longitude - Longitude of the location. * @param horizontal_accuracy - The radius of uncertainty for the location, measured in meters. 0-1500. * @param reply_to_message_id - If the message is a reply, ID of the original message. * @param reply_markup - Additional interface options. * @returns The sent Message on success. */ public async sendLocation( chat_id: string | number, latitude: number, longitude: number, horizontal_accuracy?: number, reply_to_message_id?: number, reply_markup?: InlineKeyboardMarkup | ReplyKeyboardMarkup | ReplyKeyboardRemove ): Promise<BotResponse<Message>> { const data: Record<string, any> = { chat_id, latitude, longitude }; if (horizontal_accuracy) data.horizontal_accuracy = horizontal_accuracy; if (reply_to_message_id) data.reply_to_message_id = reply_to_message_id; if (reply_markup) data.reply_markup = reply_markup; return this.sendRequest<Message>('sendLocation', data); } /** * Use this method to send phone contacts. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @param phone_number - Contact's phone number. * @param first_name - Contact's first name. * @param last_name - Contact's last name. * @param reply_to_message_id - If the message is a reply, ID of the original message. * @param reply_markup - Additional interface options. * @returns The sent Message on success. */ public async sendContact( chat_id: string | number, phone_number: string, first_name: string, last_name?: string, reply_to_message_id?: number, reply_markup?: InlineKeyboardMarkup | ReplyKeyboardMarkup | ReplyKeyboardRemove ): Promise<BotResponse<Message>> { const data: Record<string, any> = { chat_id, phone_number, first_name }; if (last_name) data.last_name = last_name; if (reply_to_message_id) data.reply_to_message_id = reply_to_message_id; if (reply_markup) data.reply_markup = reply_markup; return this.sendRequest<Message>('sendContact', data); } /** * Use this method to get basic info about a file and prepare it for downloading. * @param file_id - File identifier to get info about. * @returns A File object on success. */ public async getFile(file_id: string): Promise<BotResponse<File>> { return this.sendRequest<File>('getFile', { file_id }); } /** * Use this method to receive incoming updates using long polling. * @param options - Options for getting updates. * @returns An Array of Update objects on success. */ public async getUpdates(options?: GetUpdatesOptions): Promise<BotResponse<Update[]>> { return this.sendRequest<Update[]>('getUpdates', options); } /** * Use this method to specify a URL and receive incoming updates via an outgoing webhook. * @param options - Options for setting the webhook. * @returns True on success. */ public async setWebhook(options: SetWebhookOptions): Promise<BotResponse<boolean>> { return this.sendRequest<boolean>('setWebhook', options); } /** * Use this method to get current webhook status. * @returns A WebhookInfo object on success. */ public async getWebhookInfo(): Promise<BotResponse<WebhookInfo>> { return this.sendRequest<WebhookInfo>('getWebhookInfo'); } /** * Use this method to get up-to-date information about the chat. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @returns A Chat object on success. */ public async getChat(chat_id: string | number): Promise<BotResponse<Chat>> { return this.sendRequest<Chat>('getChat', { chat_id }); } /** * Use this method to get the number of members in a chat. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @returns An Integer representing the number of chat members. */ public async getChatMembersCount(chat_id: string | number): Promise<BotResponse<number>> { return this.sendRequest<number>('getChatMembersCount', { chat_id }); } /** * Use this method to pin a message in a group or a channel. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @param message_id - Identifier of the message to pin. * @returns True on success. */ public async pinChatMessage(chat_id: string | number, message_id: number): Promise<BotResponse<boolean>> { return this.sendRequest<boolean>('pinChatMessage', { chat_id, message_id }); } /** * Use this method to unpin a message in a group or a channel. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @param message_id - Identifier of the message to unpin. * @returns True on success. */ public async unpinChatMessage(chat_id: string | number, message_id: number): Promise<BotResponse<boolean>> { return this.sendRequest<boolean>('unpinChatMessage', { chat_id, message_id }); } /** * Use this method to unpin all pinned messages in a chat. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @returns True on success. */ public async unpinAllChatMessages(chat_id: string | number): Promise<BotResponse<boolean>> { return this.sendRequest<boolean>('unpinAllChatMessages', { chat_id }); } /** * Use this method to change the title of a chat. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @param title - New chat title. * @returns True on success. */ public async setChatTitle(chat_id: string | number, title: string): Promise<BotResponse<boolean>> { return this.sendRequest<boolean>('setChatTitle', { chat_id, title }); } /** * Use this method to change the description of a group or a channel. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @param description - New chat description. * @returns True on success. */ public async setChatDescription(chat_id: string | number, description: string): Promise<BotResponse<boolean>> { return this.sendRequest<boolean>('setChatDescription', { chat_id, description }); } /** * Use this method to delete a chat photo. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @returns True on success. */ public async deleteChatPhoto(chat_id: string | number): Promise<BotResponse<boolean>> { return this.sendRequest<boolean>('deleteChatPhoto', { chat_id }); } /** * Use this method to create a new invite link for a chat. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @returns The new invite link on success. */ public async createChatInviteLink(chat_id: string | number): Promise<BotResponse<string>> { return this.sendRequest<string>('createChatInviteLink', { chat_id }); } /** * Use this method to revoke an invite link for a group. After revocation, a new link is generated and returned. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @param invite_link - The invite link to revoke. * @returns The new invite link on success. */ public async revokeChatInviteLink(chat_id: string | number, invite_link: string): Promise<BotResponse<string>> { return this.sendRequest<string>('revokeChatInviteLink', { chat_id, invite_link }); } /** * Use this method for the bot to create a new invite link for a chat. If already created, it will be revoked and a new one will be created. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @returns The new invite link on success. */ public async exportChatInviteLink(chat_id: string | number): Promise<BotResponse<string>> { return this.sendRequest<string>('exportChatInviteLink', { chat_id }); } /** * Use this method to edit text messages. * @param text - New text of the message (1-4096 characters). * @param chat_id - Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel. * @param message_id - Required if inline_message_id is not specified. Identifier of the message to edit. * @param reply_markup - A JSON-serialized object for an inline keyboard. * @returns The edited Message object on success. */ public async editMessageText( text: string, chat_id?: string | number, message_id?: number, reply_markup?: InlineKeyboardMarkup ): Promise<BotResponse<Message>> { const data: Record<string, any> = { text }; if (chat_id) data.chat_id = chat_id; if (message_id) data.message_id = message_id; if (reply_markup) data.reply_markup = reply_markup; return this.sendRequest<Message>('editMessageText', data); } /** * Use this method to delete a message, including service messages. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @param message_id - Identifier of the message to delete. * @returns True on success. */ public async deleteMessage(chat_id: string | number, message_id: number): Promise<BotResponse<boolean>> { return this.sendRequest<boolean>('deleteMessage', { chat_id, message_id }); } /** * Use this method to upload a file for future use in newStickerSet and addStickerToSet methods. * @param user_id - User identifier of sticker file owner. * @param sticker - A file of .WEBP, .PNG, .TGS, or .WEBM format. * @returns The uploaded File object on success. */ public async uploadStickerFile(user_id: number, sticker: InputFile): Promise<BotResponse<File>> { return this.sendRequest<File>('uploadStickerFile', { user_id, sticker }, true); } /** * Use this method to create a new sticker set owned by a user. * @param user_id - User identifier of created sticker set owner. * @param name - Short name of the sticker set. * @param title - Sticker set title. * @param stickers - A JSON-serialized list of 1-50 initial stickers to be added to the sticker set. * @returns True on success. */ public async createNewStickerSet( user_id: number, name: string, title: string, stickers: InputSticker[] ): Promise<BotResponse<boolean>> { return this.sendRequest<boolean>('createNewStickerSet', { user_id, name, title, stickers: JSON.stringify(stickers) }); } /** * Use this method to add a new sticker to a set created by the bot. * @param user_id - User identifier of sticker set owner. * @param name - Sticker set name. * @param sticker - A JSON-serialized object with information about the added sticker. * @returns True on success. */ public async addStickerToSet( user_id: number, name: string, sticker: InputSticker ): Promise<BotResponse<boolean>> { return this.sendRequest<boolean>('addStickerToSet', { user_id, name, sticker: JSON.stringify(sticker) }); } /** * Use this method to send invoices. * @param chat_id - Unique identifier for the target chat or username of the target channel. * @param title - Product name (1-32 characters). * @param description - Product description (1-255 characters). * @param payload - Bot-defined invoice payload (1-128 bytes). * @param provider_token - Payment provider token (card number or wallet token). * @param prices - A JSON-serialized list of items and their prices. * @param photo_url - URL of the product photo. * @param reply_to_message_id - If the message is a reply, ID of the original message. * @returns The sent Message on success. */ public async sendInvoice( chat_id: string | number, title: string, description: string, payload: string, provider_token: string, prices: LabeledPrice[], photo_url?: string, reply_to_message_id?: number ): Promise<BotResponse<Message>> { const data: Record<string, any> = { chat_id, title, description, payload, provider_token, prices: JSON.stringify(prices) }; if (photo_url) data.photo_url = photo_url; if (reply_to_message_id) data.reply_to_message_id = reply_to_message_id; return this.sendRequest<Message>('sendInvoice', data); } /** * Use this method to respond to a pre-checkout query. * @param pre_checkout_query_id - Unique identifier for the pre-checkout query to be answered. * @param ok - True if everything is alright, False otherwise. * @param error_message - Required if ok is False. Error message to be displayed to the user. * @returns True on success. */ public async answerPreCheckoutQuery( pre_checkout_query_id: string, ok: boolean, error_message?: string ): Promise<BotResponse<boolean>> { const data: Record<string, any> = { pre_checkout_query_id, ok }; if (error_message) data.error_message = error_message; return this.sendRequest<boolean>('answerPreCheckoutQuery', data); } /** * Use this method to inquire about the status of a specific transaction by sending its unique ID. * Note: This method is NOT present in standard Telegram bot libraries and requires direct HTTP request. * @param transaction_id - Unique ID of the transaction to inquire. * @returns A Transaction object on success. */ public async inquireTransaction(transaction_id: string): Promise<BotResponse<Transaction>> { return this.sendRequest<Transaction>('inquireTransaction', { transaction_id }); } }