import createDebug from "./internal/debug.js";
import { EventEmitter } from "node:events";
import { createWriteStream } from "node:fs";
import path from "node:path";
import { Readable, PassThrough } from "node:stream";
import { pipeline } from "node:stream/promises";
import { FatalError } from "./errors.js";
import { HttpClient } from "./http.js";
import { TelegramBotPolling } from "./polling.js";
import { TelegramBotWebHook } from "./webhook.js";
import { deprecate, prepareFile, prepareFiles, stringify } from "./utils.js";
import { MESSAGE_TYPES, UPDATE_TYPES } from "./types/schemas.js";
import * as errors from "./errors.js";
const debug = createDebug("node-telegram-bot-api");
const _deprecatedMessageTypes = ["new_chat_participant", "left_chat_participant"];
/**
 * The TelegramBot class is the main entry point of the library. It provides
 * methods that map 1:1 to the Telegram Bot API and emits events for incoming
 * updates received via either long polling or a webhook server.
 *
 * @example
 * ```ts
 * const bot = new TelegramBot(token, { polling: true });
 * bot.on('message', msg => bot.sendMessage(msg.chat.id, 'echo: ' + msg.text));
 * ```
 */
export class TelegramBot extends EventEmitter {
    /** Static reference to the error classes the library throws. */
    static errors = errors;
    /** The set of message-type events the library understands. */
    static messageTypes = MESSAGE_TYPES;
    /** The Telegram Bot API token. */
    token;
    /** The fully-resolved bot configuration (both transport flags always present). */
    options;
    /** Underlying HTTP client. Accessible for advanced extensions. */
    http;
    _polling = null;
    _webHook = null;
    _textRegexpCallbacks = [];
    _replyListenerId = 0;
    _replyListeners = [];
    constructor(token, options = {}) {
        super();
        this.token = token;
        this.options = {
            ...options,
            polling: options.polling ?? false,
            webHook: options.webHook ?? false,
            baseApiUrl: options.baseApiUrl ?? "https://api.telegram.org",
            filepath: options.filepath ?? true,
            badRejection: options.badRejection ?? false,
        };
        this.http = new HttpClient(token, {
            baseApiUrl: this.options.baseApiUrl,
            testEnvironment: this.options.testEnvironment,
            request: this.options.request,
        });
        if (this.options.polling) {
            const pollingOpts = typeof this.options.polling === "boolean" ? {} : this.options.polling;
            const autoStart = pollingOpts.autoStart ?? true;
            this._polling = new TelegramBotPolling(this, pollingOpts);
            if (autoStart)
                void this._polling.start();
        }
        else if (this.options.webHook) {
            // `else if`: polling and webHook are mutually exclusive, so a config with
            // both (e.g. a JS caller bypassing the typed options) starts polling only.
            const webhookOpts = typeof this.options.webHook === "boolean" ? {} : this.options.webHook;
            const autoOpen = webhookOpts.autoOpen ?? true;
            this._webHook = new TelegramBotWebHook(this, webhookOpts);
            if (autoOpen)
                void this._webHook.open();
        }
    }
    on(event, listener) {
        if (_deprecatedMessageTypes.includes(event)) {
            const url = "https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#events";
            // eslint-disable-next-line no-console
            console.warn(`Events ${_deprecatedMessageTypes.join(",")} are deprecated. See: ${url}`);
        }
        return super.on(event, listener);
    }
    // --- internal helpers ---------------------------------------------------
    _request(method, opts = {}) {
        if (opts.form) {
            this._fixReplyMarkup(opts.form);
            this._fixEntitiesField(opts.form);
            this._fixReplyParameters(opts.form);
            this._fixMessageIds(opts.form);
            this._fixSuggestedPostParameters(opts.form);
            this._fixLinkPreviewOptions(opts.form);
            this._fixStoryAreas(opts.form);
            this._fixJsonFields(opts.form);
        }
        return this.http.request(method, opts);
    }
    _fixReplyMarkup(obj) {
        const replyMarkup = obj.reply_markup;
        if (replyMarkup && typeof replyMarkup !== "string") {
            obj.reply_markup = stringify(replyMarkup);
        }
    }
    _fixEntitiesField(obj) {
        for (const key of ["entities", "caption_entities", "explanation_entities", "description_entities", "question_entities", "title_entities", "text_entities"]) {
            const value = obj[key];
            if (value && typeof value !== "string")
                obj[key] = stringify(value);
        }
    }
    _fixReplyParameters(obj) {
        if (Object.prototype.hasOwnProperty.call(obj, "reply_parameters") &&
            typeof obj.reply_parameters !== "string") {
            obj.reply_parameters = stringify(obj.reply_parameters);
        }
    }
    _fixSuggestedPostParameters(obj) {
        if (Object.prototype.hasOwnProperty.call(obj, "suggested_post_parameters") &&
            typeof obj.suggested_post_parameters !== "string") {
            obj.suggested_post_parameters = stringify(obj.suggested_post_parameters);
        }
    }
    _fixLinkPreviewOptions(obj) {
        const value = obj.link_preview_options;
        if (value && typeof value !== "string") {
            obj.link_preview_options = stringify(value);
        }
    }
    _fixStoryAreas(obj) {
        const value = obj.areas;
        if (value && typeof value !== "string") {
            obj.areas = stringify(value);
        }
    }
    _fixMessageIds(obj) {
        const messageIds = obj.message_ids;
        if (messageIds && typeof messageIds !== "string") {
            obj.message_ids = stringify(messageIds);
        }
    }
    /**
     * JSON-stringify the remaining structured fields that the Bot API expects as
     * strings on a form body. Each is left untouched when already a string (a
     * caller may pre-serialize) or absent, so methods can pass the typed value
     * straight through and let normalization happen here.
     */
    _fixJsonFields(obj) {
        for (const key of [
            "button",
            "menu_button",
            "checklist",
            "custom_emoji_ids",
            "emoji_list",
            "errors",
            "keywords",
            "mask_position",
            "results",
            "sticker",
            "rich_message",
        ]) {
            const value = obj[key];
            if (value !== undefined && value !== null && typeof value !== "string") {
                obj[key] = stringify(value);
            }
        }
    }
    /** Submit a request whose body is a flat form. Internal helper. */
    _form(method, form) {
        return this._request(method, { form });
    }
    /**
     * Common pattern: a method that uploads exactly one file (sendPhoto / sendAudio / etc).
     * Falls back to a string when `data` is a Telegram fileId or HTTPS URL.
     */
    async _sendFile(method, fieldName, data, form, fileMeta = {}, extraThumbnail) {
        const opts = { form };
        const { file, fileId } = await prepareFile(data, fileMeta, this.options.filepath);
        if (file) {
            opts.formData = { [fieldName]: file };
        }
        else if (fileId) {
            form[fieldName] = fileId;
        }
        if (extraThumbnail) {
            const candidate = extraThumbnail.thumbnail ?? extraThumbnail.thumb;
            if (candidate) {
                const { file: thumbFile, fileId: thumbId } = await prepareFile(candidate, {}, this.options.filepath);
                if (thumbFile) {
                    opts.formData = opts.formData ?? {};
                    opts.formData["thumbnail"] = thumbFile;
                    form.thumbnail = "attach://thumbnail";
                }
                else if (thumbId) {
                    form.thumbnail = thumbId;
                }
            }
        }
        return this._request(method, opts);
    }
    /**
     * Build the `media` array + multipart `formData` shared by `sendMediaGroup`
     * and `sendPaidMedia`. Every file-bearing field of each item is treated
     * alike -- the primary `media`, plus `thumbnail` / `cover` (video, animation,
     * audio, document) and `photo` (a live photo's still image): a Buffer /
     * stream / local path is uploaded under a `<index>_<field>` part name and the
     * field is rewritten to an `attach://` reference, while a fileId / URL string
     * passes through unchanged. The caller serializes the returned `inputMedia`
     * into the request's `media` field.
     */
    async _buildMediaItems(media) {
        const formData = {};
        const inputMedia = [];
        for (const [index, item] of media.entries()) {
            // File fields are typed InputFile (which subsumes string), so each one holds
            // the incoming upload and the attach:// / file_id / URL string written back over it.
            const payload = { ...item };
            delete payload.fileOptions;
            for (const field of ["media", "thumbnail", "cover", "photo"]) {
                const value = payload[field];
                if (value === undefined || value === null)
                    continue;
                const attachName = `${index}_${field}`;
                // fileOptions (filename / contentType hints) describe the `media` upload only.
                const meta = field === "media" ? item.fileOptions : undefined;
                const { file, fileId } = await prepareFile(value, meta, this.options.filepath);
                if (file) {
                    formData[attachName] = file;
                    payload[field] = `attach://${attachName}`;
                }
                else if (fileId) {
                    payload[field] = fileId;
                }
            }
            // Every file field is now an attach:// / file_id / URL string, matching a
            // ResolvedMedia variant - which TS can't infer through the in-place rewrite.
            inputMedia.push(payload);
        }
        return { inputMedia, formData };
    }
    /**
     * Resolve an array of {@link InputStickerInput} into the docs-faithful
     * {@link InputSticker}[] wire shape plus the multipart `formData`. A
     * file-bearing `sticker` (Buffer / stream / local path) is uploaded under a
     * `sticker<index>` part name and rewritten to an `attach://` reference; a
     * file_id / URL string passes through unchanged. Shared by
     * `createNewStickerSet` and `addStickerToSet`.
     */
    async _buildStickerItems(stickers) {
        const formData = {};
        const resolved = [];
        for (const [index, item] of stickers.entries()) {
            const { sticker: data, fileOptions, ...rest } = item;
            const attachName = `sticker${index}`;
            const { file, fileId } = await prepareFile(data, fileOptions, this.options.filepath);
            if (file) {
                formData[attachName] = file;
                resolved.push({ ...rest, sticker: `attach://${attachName}` });
            }
            else {
                resolved.push({ ...rest, sticker: fileId ?? "" });
            }
        }
        return { stickers: resolved, formData };
    }
    // --- High-level lifecycle ----------------------------------------------
    /** Start polling. */
    async startPolling(options = {}) {
        if (this.hasOpenWebHook()) {
            throw new FatalError("Polling and WebHook are mutually exclusive");
        }
        if (!this._polling) {
            const pollingOpts = typeof this.options.polling === "object" ? this.options.polling : {};
            this._polling = new TelegramBotPolling(this, pollingOpts);
        }
        return this._polling.start({ restart: options.restart ?? false });
    }
    /** Stop polling. */
    async stopPolling(options = {}) {
        if (!this._polling)
            return;
        return this._polling.stop(options);
    }
    isPolling() {
        return this._polling?.isPolling() ?? false;
    }
    async openWebHook() {
        if (this.isPolling())
            throw new FatalError("WebHook and Polling are mutually exclusive");
        if (!this._webHook) {
            const webhookOpts = typeof this.options.webHook === "object" ? this.options.webHook : {};
            this._webHook = new TelegramBotWebHook(this, webhookOpts);
        }
        return this._webHook.open();
    }
    async closeWebHook() {
        if (!this._webHook)
            return;
        return this._webHook.close();
    }
    hasOpenWebHook() {
        return this._webHook?.isOpen() ?? false;
    }
    // --- Reply / regexp listeners ------------------------------------------
    onText(regexp, callback) {
        const compiled = regexp instanceof RegExp ? regexp : new RegExp(regexp);
        this._textRegexpCallbacks.push({ regexp: compiled, callback });
    }
    removeTextListener(regexp) {
        const index = this._textRegexpCallbacks.findIndex((listener) => String(listener.regexp) === String(regexp));
        if (index === -1)
            return null;
        return this._textRegexpCallbacks.splice(index, 1)[0] ?? null;
    }
    clearTextListeners() {
        this._textRegexpCallbacks = [];
    }
    onReplyToMessage(chatId, messageId, callback) {
        const id = ++this._replyListenerId;
        this._replyListeners.push({ id, chatId, messageId, callback });
        return id;
    }
    removeReplyListener(replyListenerId) {
        const index = this._replyListeners.findIndex((entry) => entry.id === replyListenerId);
        if (index === -1)
            return null;
        return this._replyListeners.splice(index, 1)[0] ?? null;
    }
    clearReplyListeners() {
        const removed = this._replyListeners;
        this._replyListeners = [];
        return removed;
    }
    // --- Update processing -------------------------------------------------
    /**
     * Dispatch a single Update. Use this if you obtain updates from a source other
     * than this library's polling/webhook (e.g. AWS Lambda, custom proxy, tests).
     */
    processUpdate(update) {
        debug("Process Update %j", update);
        const m = update.message;
        if (m) {
            const metadata = { type: TelegramBot.messageTypes.find((t) => m[t]) };
            this.emit("message", m, metadata);
            if (metadata.type)
                this.emit(metadata.type, m, metadata);
            if (m.text) {
                for (const reg of this._textRegexpCallbacks) {
                    const result = reg.regexp.exec(m.text);
                    if (!result)
                        continue;
                    reg.regexp.lastIndex = 0;
                    reg.callback(m, result);
                    if (this.options.onlyFirstMatch)
                        break;
                }
            }
            if (m.reply_to_message) {
                for (const reply of this._replyListeners) {
                    if (reply.chatId === m.chat.id && reply.messageId === m.reply_to_message.message_id) {
                        reply.callback(m);
                    }
                }
            }
            return;
        }
        // Every non-message update maps 1:1 to an event of the same name. Iterating
        // the generated UPDATE_TYPES (every `Update` field) keeps this exhaustive by
        // construction: a newly documented update kind is dispatched automatically
        // once the types are regenerated, with no hand-maintained mirror to forget.
        // `message` needs no special skip here - when it is present we return above,
        // and when it is absent `update.message` is undefined and falls through.
        for (const key of UPDATE_TYPES) {
            const value = update[key];
            if (value === undefined)
                continue;
            debug("Process Update %s %j", key, value);
            this.emit(key, value);
            // Special-case sub-events for edited messages
            if (key === "edited_message") {
                const em = value;
                if (em.text)
                    this.emit("edited_message_text", em);
                if (em.caption)
                    this.emit("edited_message_caption", em);
            }
            if (key === "edited_channel_post") {
                const ep = value;
                if (ep.text)
                    this.emit("edited_channel_post_text", ep);
                if (ep.caption)
                    this.emit("edited_channel_post_caption", ep);
            }
            return;
        }
    }
    // ===================================================================
    // Telegram Bot API methods
    // ===================================================================
    // --- Files & downloads -------------------------------------------------
    /** Resolve a file id to the public download URL on Telegram's servers. */
    async getFileLink(fileId, options = {}) {
        const file = await this.getFile(fileId, options);
        return `${this.options.baseApiUrl}/file/bot${this.token}/${file.file_path}`;
    }
    /**
     * Stream the contents of a Telegram file. The returned stream emits an `info`
     * event with the resolved URI before the bytes start flowing.
     */
    getFileStream(fileId, options = {}) {
        const out = new PassThrough();
        out.path = fileId;
        void (async () => {
            try {
                const uri = await this.getFileLink(fileId, options);
                out.emit("info", { uri });
                const response = await fetch(uri);
                if (!response.ok || !response.body) {
                    throw new FatalError(`Failed to fetch file: HTTP ${response.status}`);
                }
                await pipeline(Readable.fromWeb(response.body), out);
            }
            catch (err) {
                out.emit("error", err);
            }
        })();
        return out;
    }
    /**
     * Download a Telegram file to a local directory and resolve to the resulting path.
     */
    async downloadFile(fileId, downloadDir, options = {}) {
        const uri = await this.getFileLink(fileId, options);
        const fileName = uri.slice(uri.lastIndexOf("/") + 1);
        const filePath = path.join(downloadDir, fileName);
        const response = await fetch(uri);
        if (!response.ok || !response.body) {
            throw new FatalError(`Failed to download file: HTTP ${response.status}`);
        }
        const out = createWriteStream(filePath);
        await pipeline(Readable.fromWeb(response.body), out);
        return filePath;
    }
    // --- Updates / webhook -------------------------------------------------
    getUpdates(form = {}) {
        if (Array.isArray(form.allowed_updates)) {
            form.allowed_updates = stringify(form.allowed_updates);
        }
        return this._form("getUpdates", form);
    }
    async setWebhook(url, options = {}, fileOptions = {}) {
        const { certificate, ...rest } = options;
        const form = { ...rest, url };
        if (Array.isArray(form.allowed_updates))
            form.allowed_updates = stringify(form.allowed_updates);
        if (certificate) {
            const { file, fileId } = await prepareFile(certificate, fileOptions, this.options.filepath);
            if (file) {
                return this._request("setWebhook", { form, formData: { certificate: file } });
            }
            form.certificate = fileId;
        }
        return this._request("setWebhook", { form });
    }
    deleteWebhook(form = {}) {
        return this._form("deleteWebhook", form);
    }
    getWebhookInfo(form = {}) {
        return this._form("getWebhookInfo", form);
    }
    /** @deprecated Renamed to {@link TelegramBot.setWebhook} to match the Bot API casing. */
    setWebHook(url, options = {}, fileOptions = {}) {
        deprecate("setWebHook(...) is deprecated; use setWebhook(...) instead.");
        return this.setWebhook(url, options, fileOptions);
    }
    /** @deprecated Renamed to {@link TelegramBot.deleteWebhook} to match the Bot API casing. */
    deleteWebHook(form = {}) {
        deprecate("deleteWebHook(...) is deprecated; use deleteWebhook(...) instead.");
        return this.deleteWebhook(form);
    }
    /** @deprecated Renamed to {@link TelegramBot.getWebhookInfo} to match the Bot API casing. */
    getWebHookInfo(form = {}) {
        deprecate("getWebHookInfo(...) is deprecated; use getWebhookInfo(...) instead.");
        return this.getWebhookInfo(form);
    }
    // --- Bot identity ------------------------------------------------------
    getMe(form = {}) {
        return this._form("getMe", form);
    }
    logOut(form = {}) {
        return this._form("logOut", form);
    }
    close(form = {}) {
        return this._form("close", form);
    }
    // --- Messages ----------------------------------------------------------
    sendMessage(chatId, text, form = {}) {
        return this._form("sendMessage", {
            ...form,
            chat_id: chatId,
            text,
        });
    }
    sendRichMessage(chatId, richMessage, form = {}) {
        return this._form("sendRichMessage", {
            ...form,
            chat_id: chatId,
            rich_message: richMessage,
        });
    }
    forwardMessage(chatId, fromChatId, messageId, form = {}) {
        return this._form("forwardMessage", {
            ...form,
            chat_id: chatId,
            from_chat_id: fromChatId,
            message_id: messageId,
        });
    }
    forwardMessages(chatId, fromChatId, messageIds, form = {}) {
        return this._form("forwardMessages", {
            ...form,
            chat_id: chatId,
            from_chat_id: fromChatId,
            message_ids: messageIds,
        });
    }
    copyMessage(chatId, fromChatId, messageId, form = {}) {
        return this._form("copyMessage", {
            ...form,
            chat_id: chatId,
            from_chat_id: fromChatId,
            message_id: messageId,
        });
    }
    copyMessages(chatId, fromChatId, messageIds, form = {}) {
        return this._form("copyMessages", {
            ...form,
            chat_id: chatId,
            from_chat_id: fromChatId,
            message_ids: stringify(messageIds),
        });
    }
    // --- Send file methods -------------------------------------------------
    sendPhoto(chatId, photo, options = {}, fileOptions = {}) {
        return this._sendFile("sendPhoto", "photo", photo, { ...options, chat_id: chatId }, fileOptions);
    }
    async sendLivePhoto(chatId, livePhoto, photo, options = {}, fileOptions = {}) {
        const form = { ...options, chat_id: chatId };
        const opts = { form };
        const liveResult = await prepareFile(livePhoto, fileOptions, this.options.filepath);
        if (liveResult.file) {
            opts.formData = { live_photo: liveResult.file };
            form.live_photo = "attach://live_photo";
        }
        else if (liveResult.fileId) {
            form.live_photo = liveResult.fileId;
        }
        const photoResult = await prepareFile(photo, {}, this.options.filepath);
        if (photoResult.file) {
            opts.formData = opts.formData ?? {};
            opts.formData.photo = photoResult.file;
            form.photo = "attach://photo";
        }
        else if (photoResult.fileId) {
            form.photo = photoResult.fileId;
        }
        return this._request("sendLivePhoto", opts);
    }
    sendAudio(chatId, audio, options = {}, fileOptions = {}) {
        return this._sendFile("sendAudio", "audio", audio, { ...options, chat_id: chatId }, fileOptions, { thumbnail: options.thumbnail });
    }
    sendDocument(chatId, doc, options = {}, fileOptions = {}) {
        return this._sendFile("sendDocument", "document", doc, { ...options, chat_id: chatId }, fileOptions, { thumbnail: options.thumbnail });
    }
    sendVideo(chatId, video, options = {}, fileOptions = {}) {
        return this._sendFile("sendVideo", "video", video, { ...options, chat_id: chatId }, fileOptions, { thumbnail: options.thumbnail });
    }
    sendAnimation(chatId, animation, options = {}, fileOptions = {}) {
        return this._sendFile("sendAnimation", "animation", animation, { ...options, chat_id: chatId }, fileOptions, { thumbnail: options.thumbnail });
    }
    sendVoice(chatId, voice, options = {}, fileOptions = {}) {
        return this._sendFile("sendVoice", "voice", voice, { ...options, chat_id: chatId }, fileOptions);
    }
    sendVideoNote(chatId, videoNote, options = {}, fileOptions = {}) {
        return this._sendFile("sendVideoNote", "video_note", videoNote, { ...options, chat_id: chatId }, fileOptions, { thumbnail: options.thumbnail });
    }
    /**
     * Send paid media. Each item's file fields are widened to accept uploads: the
     * primary `media` plus any `thumbnail` / `cover` (video) or `photo` (live photo)
     * may be a Buffer / stream / local path (uploaded as a multipart part) or a
     * file_id / URL string (passed through). {@link _buildMediaItems} resolves them.
     */
    async sendPaidMedia(chatId, starCount, media, options = {}) {
        const form = { ...options, chat_id: chatId, star_count: starCount };
        const { inputMedia, formData } = await this._buildMediaItems(media);
        form.media = stringify(inputMedia);
        return this._request("sendPaidMedia", { form, formData });
    }
    /**
     * Send a group of photos / videos / etc as an album. Each item's file fields are
     * widened to accept uploads: the primary `media` plus any `thumbnail` / `cover`
     * (video) or `photo` (live photo) may be a Buffer / stream / local path (uploaded
     * as a multipart part) or a file_id / URL string (passed through).
     * {@link _buildMediaItems} resolves every file field of every item, shared with
     * {@link sendPaidMedia} and {@link editMessageMedia}.
     */
    async sendMediaGroup(chatId, media, options = {}) {
        const form = { ...options, chat_id: chatId };
        const { inputMedia, formData } = await this._buildMediaItems(media);
        form.media = stringify(inputMedia);
        return this._request("sendMediaGroup", { form, formData });
    }
    sendLocation(chatId, latitude, longitude, form = {}) {
        return this._form("sendLocation", {
            ...form,
            chat_id: chatId,
            latitude,
            longitude,
        });
    }
    sendVenue(chatId, latitude, longitude, title, address, form = {}) {
        return this._form("sendVenue", {
            ...form,
            chat_id: chatId,
            latitude,
            longitude,
            title,
            address,
        });
    }
    sendContact(chatId, phoneNumber, firstName, form = {}) {
        return this._form("sendContact", {
            ...form,
            chat_id: chatId,
            phone_number: phoneNumber,
            first_name: firstName,
        });
    }
    sendPoll(chatId, question, pollOptions, form = {}) {
        const out = { ...form, chat_id: chatId, question, options: stringify(pollOptions) };
        if (out.country_codes)
            out.country_codes = stringify(out.country_codes);
        if (out.correct_option_ids)
            out.correct_option_ids = stringify(out.correct_option_ids);
        return this._form("sendPoll", out);
    }
    sendChecklist(businessConnectionId, chatId, checklist, form = {}) {
        return this._form("sendChecklist", {
            ...form,
            business_connection_id: businessConnectionId,
            chat_id: chatId,
            checklist: stringify(checklist),
        });
    }
    sendDice(chatId, options = {}) {
        return this._form("sendDice", {
            ...options,
            chat_id: chatId,
        });
    }
    sendMessageDraft(chatId, draftId, text, form = {}) {
        return this._form("sendMessageDraft", {
            ...form,
            chat_id: chatId,
            draft_id: draftId,
            text,
        });
    }
    sendRichMessageDraft(chatId, draftId, richMessage, form = {}) {
        return this._form("sendRichMessageDraft", {
            ...form,
            chat_id: chatId,
            draft_id: draftId,
            rich_message: richMessage,
        });
    }
    sendChatAction(chatId, action, form = {}) {
        return this._form("sendChatAction", {
            ...form,
            chat_id: chatId,
            action,
        });
    }
    setMessageReaction(chatId, messageId, form = {}) {
        const out = { ...form, chat_id: chatId, message_id: messageId };
        if (out.reaction)
            out.reaction = stringify(out.reaction);
        return this._form("setMessageReaction", out);
    }
    editMessageLiveLocation(latitude, longitude, form = {}) {
        return this._form("editMessageLiveLocation", {
            ...form,
            latitude,
            longitude,
        });
    }
    stopMessageLiveLocation(form = {}) {
        return this._form("stopMessageLiveLocation", form);
    }
    // --- Users -------------------------------------------------------------
    getUserProfilePhotos(userId, form = {}) {
        return this._form("getUserProfilePhotos", {
            ...form,
            user_id: userId,
        });
    }
    getUserProfileAudios(userId, form = {}) {
        return this._form("getUserProfileAudios", {
            ...form,
            user_id: userId,
        });
    }
    setUserEmojiStatus(userId, form = {}) {
        return this._form("setUserEmojiStatus", {
            ...form,
            user_id: userId,
        });
    }
    getFile(fileId, form = {}) {
        return this._form("getFile", {
            ...form,
            file_id: fileId,
        });
    }
    getUserPersonalChatMessages(userId, limit) {
        return this._form("getUserPersonalChatMessages", {
            user_id: userId,
            limit,
        });
    }
    // --- Chat membership --------------------------------------------------
    banChatMember(chatId, userId, form = {}) {
        return this._form("banChatMember", {
            ...form,
            chat_id: chatId,
            user_id: userId,
        });
    }
    unbanChatMember(chatId, userId, form = {}) {
        return this._form("unbanChatMember", {
            ...form,
            chat_id: chatId,
            user_id: userId,
        });
    }
    restrictChatMember(chatId, userId, permissions, form = {}) {
        return this._form("restrictChatMember", {
            ...form,
            chat_id: chatId,
            user_id: userId,
            permissions: stringify(permissions),
        });
    }
    promoteChatMember(chatId, userId, form = {}) {
        return this._form("promoteChatMember", {
            ...form,
            chat_id: chatId,
            user_id: userId,
        });
    }
    setChatAdministratorCustomTitle(chatId, userId, customTitle, form = {}) {
        return this._form("setChatAdministratorCustomTitle", {
            ...form,
            chat_id: chatId,
            user_id: userId,
            custom_title: customTitle,
        });
    }
    setChatMemberTag(chatId, userId, form = {}) {
        return this._form("setChatMemberTag", {
            ...form,
            chat_id: chatId,
            user_id: userId,
        });
    }
    banChatSenderChat(chatId, senderChatId, form = {}) {
        return this._form("banChatSenderChat", {
            ...form,
            chat_id: chatId,
            sender_chat_id: senderChatId,
        });
    }
    unbanChatSenderChat(chatId, senderChatId, form = {}) {
        return this._form("unbanChatSenderChat", {
            ...form,
            chat_id: chatId,
            sender_chat_id: senderChatId,
        });
    }
    setChatPermissions(chatId, permissions, form = {}) {
        return this._form("setChatPermissions", {
            ...form,
            chat_id: chatId,
            permissions: stringify(permissions),
        });
    }
    // --- Chat invite links ------------------------------------------------
    exportChatInviteLink(chatId, form = {}) {
        return this._form("exportChatInviteLink", {
            ...form,
            chat_id: chatId,
        });
    }
    createChatInviteLink(chatId, form = {}) {
        return this._form("createChatInviteLink", {
            ...form,
            chat_id: chatId,
        });
    }
    editChatInviteLink(chatId, inviteLink, form = {}) {
        return this._form("editChatInviteLink", {
            ...form,
            chat_id: chatId,
            invite_link: inviteLink,
        });
    }
    createChatSubscriptionInviteLink(chatId, subscriptionPeriod, subscriptionPrice, form = {}) {
        return this._form("createChatSubscriptionInviteLink", {
            ...form,
            chat_id: chatId,
            subscription_period: subscriptionPeriod,
            subscription_price: subscriptionPrice,
        });
    }
    editChatSubscriptionInviteLink(chatId, inviteLink, form = {}) {
        return this._form("editChatSubscriptionInviteLink", {
            ...form,
            chat_id: chatId,
            invite_link: inviteLink,
        });
    }
    revokeChatInviteLink(chatId, inviteLink, form = {}) {
        return this._form("revokeChatInviteLink", {
            ...form,
            chat_id: chatId,
            invite_link: inviteLink,
        });
    }
    approveChatJoinRequest(chatId, userId, form = {}) {
        return this._form("approveChatJoinRequest", {
            ...form,
            chat_id: chatId,
            user_id: userId,
        });
    }
    declineChatJoinRequest(chatId, userId, form = {}) {
        return this._form("declineChatJoinRequest", {
            ...form,
            chat_id: chatId,
            user_id: userId,
        });
    }
    answerChatJoinRequestQuery(chatJoinRequestQueryId, result, form = {}) {
        return this._form("answerChatJoinRequestQuery", {
            ...form,
            chat_join_request_query_id: chatJoinRequestQueryId,
            result,
        });
    }
    sendChatJoinRequestWebApp(chatJoinRequestQueryId, webAppUrl, form = {}) {
        return this._form("sendChatJoinRequestWebApp", {
            ...form,
            chat_join_request_query_id: chatJoinRequestQueryId,
            web_app_url: webAppUrl,
        });
    }
    // --- Chat metadata ---------------------------------------------------
    /**
     * Set the chat photo. The photo must be a JPEG: Telegram's chat-photo backend
     * silently stalls on a non-JPEG (e.g. a PNG) until the request times out
     * (EFATAL); no layer here validates or converts, so the caller must pass a JPEG.
     */
    setChatPhoto(chatId, photo, options = {}, fileOptions = {}) {
        return this._sendFile("setChatPhoto", "photo", photo, { ...options, chat_id: chatId }, fileOptions);
    }
    deleteChatPhoto(chatId, form = {}) {
        return this._form("deleteChatPhoto", {
            ...form,
            chat_id: chatId,
        });
    }
    setChatTitle(chatId, title, form = {}) {
        return this._form("setChatTitle", {
            ...form,
            chat_id: chatId,
            title,
        });
    }
    setChatDescription(chatId, description, form = {}) {
        return this._form("setChatDescription", {
            ...form,
            chat_id: chatId,
            description,
        });
    }
    pinChatMessage(chatId, messageId, form = {}) {
        return this._form("pinChatMessage", {
            ...form,
            chat_id: chatId,
            message_id: messageId,
        });
    }
    unpinChatMessage(chatId, form = {}) {
        return this._form("unpinChatMessage", {
            ...form,
            chat_id: chatId,
        });
    }
    unpinAllChatMessages(chatId, form = {}) {
        return this._form("unpinAllChatMessages", {
            ...form,
            chat_id: chatId,
        });
    }
    leaveChat(chatId, form = {}) {
        return this._form("leaveChat", {
            ...form,
            chat_id: chatId,
        });
    }
    getChat(chatId, form = {}) {
        return this._form("getChat", {
            ...form,
            chat_id: chatId,
        });
    }
    getChatAdministrators(chatId, form = {}) {
        return this._form("getChatAdministrators", {
            ...form,
            chat_id: chatId,
        });
    }
    getChatMemberCount(chatId, form = {}) {
        return this._form("getChatMemberCount", {
            ...form,
            chat_id: chatId,
        });
    }
    getChatMember(chatId, userId, form = {}) {
        return this._form("getChatMember", {
            ...form,
            chat_id: chatId,
            user_id: userId,
        });
    }
    setChatStickerSet(chatId, stickerSetName, form = {}) {
        return this._form("setChatStickerSet", {
            ...form,
            chat_id: chatId,
            sticker_set_name: stickerSetName,
        });
    }
    deleteChatStickerSet(chatId, form = {}) {
        return this._form("deleteChatStickerSet", {
            ...form,
            chat_id: chatId,
        });
    }
    // --- Forum topics -----------------------------------------------------
    getForumTopicIconStickers(form = {}) {
        return this._form("getForumTopicIconStickers", form);
    }
    createForumTopic(chatId, name, form = {}) {
        return this._form("createForumTopic", {
            ...form,
            chat_id: chatId,
            name,
        });
    }
    editForumTopic(chatId, messageThreadId, form = {}) {
        return this._form("editForumTopic", {
            ...form,
            chat_id: chatId,
            message_thread_id: messageThreadId,
        });
    }
    closeForumTopic(chatId, messageThreadId, form = {}) {
        return this._form("closeForumTopic", {
            ...form,
            chat_id: chatId,
            message_thread_id: messageThreadId,
        });
    }
    reopenForumTopic(chatId, messageThreadId, form = {}) {
        return this._form("reopenForumTopic", {
            ...form,
            chat_id: chatId,
            message_thread_id: messageThreadId,
        });
    }
    deleteForumTopic(chatId, messageThreadId, form = {}) {
        return this._form("deleteForumTopic", {
            ...form,
            chat_id: chatId,
            message_thread_id: messageThreadId,
        });
    }
    unpinAllForumTopicMessages(chatId, messageThreadId, form = {}) {
        return this._form("unpinAllForumTopicMessages", {
            ...form,
            chat_id: chatId,
            message_thread_id: messageThreadId,
        });
    }
    editGeneralForumTopic(chatId, name, form = {}) {
        return this._form("editGeneralForumTopic", {
            ...form,
            chat_id: chatId,
            name,
        });
    }
    closeGeneralForumTopic(chatId, form = {}) {
        return this._form("closeGeneralForumTopic", {
            ...form,
            chat_id: chatId,
        });
    }
    reopenGeneralForumTopic(chatId, form = {}) {
        return this._form("reopenGeneralForumTopic", {
            ...form,
            chat_id: chatId,
        });
    }
    hideGeneralForumTopic(chatId, form = {}) {
        return this._form("hideGeneralForumTopic", {
            ...form,
            chat_id: chatId,
        });
    }
    unhideGeneralForumTopic(chatId, form = {}) {
        return this._form("unhideGeneralForumTopic", {
            ...form,
            chat_id: chatId,
        });
    }
    unpinAllGeneralForumTopicMessages(chatId, form = {}) {
        return this._form("unpinAllGeneralForumTopicMessages", {
            ...form,
            chat_id: chatId,
        });
    }
    // --- Callback / inline queries ---------------------------------------
    answerCallbackQuery(callbackQueryId, form = {}) {
        return this._form("answerCallbackQuery", {
            ...form,
            callback_query_id: callbackQueryId,
        });
    }
    answerGuestQuery(guestQueryId, result) {
        return this._form("answerGuestQuery", {
            guest_query_id: guestQueryId,
            result: stringify(result),
        });
    }
    savePreparedInlineMessage(userId, result, form = {}) {
        return this._form("savePreparedInlineMessage", {
            ...form,
            user_id: userId,
            result: stringify(result),
        });
    }
    savePreparedKeyboardButton(userId, button, form = {}) {
        return this._form("savePreparedKeyboardButton", {
            ...form,
            user_id: userId,
            button,
        });
    }
    getUserChatBoosts(chatId, userId, form = {}) {
        return this._form("getUserChatBoosts", {
            ...form,
            chat_id: chatId,
            user_id: userId,
        });
    }
    getBusinessConnection(businessConnectionId, form = {}) {
        return this._form("getBusinessConnection", {
            ...form,
            business_connection_id: businessConnectionId,
        });
    }
    getManagedBotToken(userId, form = {}) {
        return this._form("getManagedBotToken", {
            ...form,
            user_id: userId,
        });
    }
    replaceManagedBotToken(userId, form = {}) {
        return this._form("replaceManagedBotToken", {
            ...form,
            user_id: userId,
        });
    }
    getManagedBotAccessSettings(userId, form = {}) {
        return this._form("getManagedBotAccessSettings", {
            ...form,
            user_id: userId,
        });
    }
    setManagedBotAccessSettings(userId, isAccessRestricted, form = {}) {
        const out = {
            user_id: userId,
            is_access_restricted: isAccessRestricted,
        };
        if (form.added_user_ids)
            out.added_user_ids = stringify(form.added_user_ids);
        return this._form("setManagedBotAccessSettings", out);
    }
    // --- Bot identity (self-management) ----------------------------------
    setMyCommands(commands, form = {}) {
        const out = { ...form, commands: stringify(commands) };
        if (out.scope)
            out.scope = stringify(out.scope);
        return this._form("setMyCommands", out);
    }
    deleteMyCommands(form = {}) {
        const out = { ...form };
        if (out.scope)
            out.scope = stringify(out.scope);
        return this._form("deleteMyCommands", out);
    }
    getMyCommands(form = {}) {
        const out = { ...form };
        if (out.scope)
            out.scope = stringify(out.scope);
        return this._form("getMyCommands", out);
    }
    setMyName(form = {}) {
        return this._form("setMyName", form);
    }
    getMyName(form = {}) {
        return this._form("getMyName", form);
    }
    setMyDescription(form = {}) {
        return this._form("setMyDescription", form);
    }
    getMyDescription(form = {}) {
        return this._form("getMyDescription", form);
    }
    setMyShortDescription(form = {}) {
        return this._form("setMyShortDescription", form);
    }
    getMyShortDescription(form = {}) {
        return this._form("getMyShortDescription", form);
    }
    /**
     * Set the bot's profile photo.
     *
     * ⚠️ A `static` photo **must be a JPEG**. Telegram's backend
     * (`photos.uploadProfilePhoto`) only accepts JPEG and no layer in the stack
     * — this library, the Bot API server, or TDLib — validates or converts the
     * format, so the raw bytes are forwarded as-is. A non-JPEG (e.g. PNG) is not
     * rejected cleanly: the upstream call typically returns `504 Gateway Timeout`
     * (an `ETELEGRAM` error) or stalls until the request times out. Convert to
     * JPEG before calling.
     */
    async setMyProfilePhoto(photo, form = {}) {
        const fieldName = photo.type === "static" ? "photo" : "animation";
        const fileInput = photo[fieldName];
        const { file } = await prepareFile(fileInput, {}, this.options.filepath);
        if (!file) {
            throw new FatalError("setMyProfilePhoto only supports file uploads (Buffer, Stream, or local path)");
        }
        const struct = { ...photo, [fieldName]: `attach://${fieldName}` };
        return this._request("setMyProfilePhoto", {
            form: { ...form, photo: stringify(struct) },
            formData: { [fieldName]: file },
        });
    }
    removeMyProfilePhoto(form = {}) {
        return this._form("removeMyProfilePhoto", form);
    }
    setChatMenuButton(form = {}) {
        return this._form("setChatMenuButton", form);
    }
    getChatMenuButton(form = {}) {
        return this._form("getChatMenuButton", form);
    }
    setMyDefaultAdministratorRights(form = {}) {
        return this._form("setMyDefaultAdministratorRights", form);
    }
    getMyDefaultAdministratorRights(form = {}) {
        return this._form("getMyDefaultAdministratorRights", form);
    }
    editMessageText(textOrForm, form = {}) {
        if (typeof textOrForm === "string") {
            return this._form("editMessageText", { ...form, text: textOrForm });
        }
        return this._form("editMessageText", textOrForm);
    }
    editMessageCaption(caption, form = {}) {
        return this._form("editMessageCaption", {
            ...form,
            caption,
        });
    }
    /**
     * Edit a message's media. The `media` (and its `thumbnail` / `cover`) accept a
     * file (Buffer / stream / local path, uploaded via an `attach://` part) or a
     * file_id / URL string (passed through), resolved by {@link _buildMediaItems}
     * like {@link sendMediaGroup}. The legacy `attach://<local-path>` form is still
     * accepted for the primary `media`.
     */
    async editMessageMedia(media, form = {}) {
        const item = { ...media };
        // Back-compat: the old API took the new file as `attach://<local-path>`. Strip
        // the prefix so the path is uploaded like any other file input.
        if (typeof item.media === "string" && item.media.startsWith("attach://")) {
            item.media = item.media.slice("attach://".length);
        }
        const { inputMedia, formData } = await this._buildMediaItems([item]);
        const out = {
            ...form,
            media: stringify(inputMedia[0]),
        };
        return this._request("editMessageMedia", { form: out, formData });
    }
    editMessageChecklist(businessConnectionId, chatId, messageId, checklist, form = {}) {
        return this._form("editMessageChecklist", {
            ...form,
            business_connection_id: businessConnectionId,
            chat_id: chatId,
            message_id: messageId,
            checklist,
        });
    }
    editMessageReplyMarkup(replyMarkup, form = {}) {
        return this._form("editMessageReplyMarkup", {
            ...form,
            reply_markup: replyMarkup,
        });
    }
    stopPoll(chatId, pollId, form = {}) {
        return this._form("stopPoll", {
            ...form,
            chat_id: chatId,
            message_id: pollId,
        });
    }
    // --- Suggested posts --------------------------------------------------
    approveSuggestedPost(chatId, messageId, form = {}) {
        return this._form("approveSuggestedPost", {
            ...form,
            chat_id: chatId,
            message_id: messageId,
        });
    }
    declineSuggestedPost(chatId, messageId, form = {}) {
        return this._form("declineSuggestedPost", {
            ...form,
            chat_id: chatId,
            message_id: messageId,
        });
    }
    // --- Stickers --------------------------------------------------------
    sendSticker(chatId, sticker, options = {}, fileOptions = {}) {
        return this._sendFile("sendSticker", "sticker", sticker, { ...options, chat_id: chatId }, fileOptions);
    }
    getStickerSet(name, form = {}) {
        return this._form("getStickerSet", {
            ...form,
            name,
        });
    }
    getCustomEmojiStickers(customEmojiIds, form = {}) {
        return this._form("getCustomEmojiStickers", {
            ...form,
            custom_emoji_ids: customEmojiIds,
        });
    }
    uploadStickerFile(userId, sticker, stickerFormat = "static", options = {}, fileOptions = {}) {
        return this._sendFile("uploadStickerFile", "sticker", sticker, { ...options, user_id: userId, sticker_format: stickerFormat }, fileOptions);
    }
    /**
     * Create a new sticker set owned by a user. Each entry in `stickers` is an
     * {@link InputStickerInput} whose `sticker` accepts a file (Buffer / stream /
     * local path, uploaded as an `attach://` part) or a file_id / URL string, plus
     * the sticker's `format`, `emoji_list`, and optional `mask_position`/`keywords`.
     * @see https://core.telegram.org/bots/api#createnewstickerset
     */
    async createNewStickerSet(params) {
        const { stickers: stickerInputs, ...rest } = params;
        const { stickers, formData } = await this._buildStickerItems(stickerInputs);
        const form = { ...rest };
        form.stickers = stringify(stickers);
        return this._request("createNewStickerSet", { form, formData });
    }
    /**
     * Add a sticker to an existing set. The `sticker` is an
     * {@link InputStickerInput} whose `sticker` field accepts a file (Buffer /
     * stream / local path, uploaded as an `attach://` part) or a file_id / URL string.
     * @see https://core.telegram.org/bots/api#addstickertoset
     */
    async addStickerToSet(params) {
        const { sticker, ...rest } = params;
        const { stickers, formData } = await this._buildStickerItems([sticker]);
        const form = { ...rest };
        form.sticker = stringify(stickers[0]);
        return this._request("addStickerToSet", { form, formData });
    }
    setStickerPositionInSet(sticker, position, form = {}) {
        return this._form("setStickerPositionInSet", {
            ...form,
            sticker,
            position,
        });
    }
    deleteStickerFromSet(sticker, form = {}) {
        return this._form("deleteStickerFromSet", {
            ...form,
            sticker,
        });
    }
    replaceStickerInSet(userId, name, oldSticker, form) {
        return this._form("replaceStickerInSet", {
            ...form,
            user_id: userId,
            name,
            old_sticker: oldSticker,
        });
    }
    setStickerEmojiList(sticker, emojiList, form = {}) {
        return this._form("setStickerEmojiList", {
            ...form,
            sticker,
            emoji_list: emojiList,
        });
    }
    setStickerKeywords(sticker, form = {}) {
        return this._form("setStickerKeywords", { ...form, sticker });
    }
    setStickerMaskPosition(sticker, form = {}) {
        return this._form("setStickerMaskPosition", { ...form, sticker });
    }
    setStickerSetTitle(name, title, form = {}) {
        return this._form("setStickerSetTitle", {
            ...form,
            name,
            title,
        });
    }
    setStickerSetThumbnail(userId, name, thumbnail, options = {}, fileOptions = {}) {
        return this._sendFile("setStickerSetThumbnail", "thumbnail", thumbnail, { ...options, user_id: userId, name }, fileOptions);
    }
    setCustomEmojiStickerSetThumbnail(name, form = {}) {
        return this._form("setCustomEmojiStickerSetThumbnail", {
            ...form,
            name,
        });
    }
    deleteStickerSet(name, form = {}) {
        return this._form("deleteStickerSet", {
            ...form,
            name,
        });
    }
    // --- Inline / web app -------------------------------------------------
    answerInlineQuery(inlineQueryId, results, form = {}) {
        return this._form("answerInlineQuery", {
            ...form,
            inline_query_id: inlineQueryId,
            results,
        });
    }
    answerWebAppQuery(webAppQueryId, result, form = {}) {
        return this._form("answerWebAppQuery", {
            ...form,
            web_app_query_id: webAppQueryId,
            result: stringify(result),
        });
    }
    // --- Payments --------------------------------------------------------
    sendInvoice(chatId, title, description, payload, providerToken, currency, prices, form = {}) {
        const out = {
            ...form,
            chat_id: chatId,
            title,
            description,
            payload,
            provider_token: providerToken,
            currency,
            prices: stringify(prices),
        };
        if (out.provider_data !== undefined)
            out.provider_data = stringify(out.provider_data);
        if (out.suggested_tip_amounts)
            out.suggested_tip_amounts = stringify(out.suggested_tip_amounts);
        return this._form("sendInvoice", out);
    }
    createInvoiceLink(title, description, payload, providerToken, currency, prices, form = {}) {
        return this._form("createInvoiceLink", {
            ...form,
            title,
            description,
            payload,
            provider_token: providerToken,
            currency,
            prices: stringify(prices),
        });
    }
    answerShippingQuery(shippingQueryId, ok, form = {}) {
        const out = { ...form, shipping_query_id: shippingQueryId, ok };
        if (out.shipping_options)
            out.shipping_options = stringify(out.shipping_options);
        return this._form("answerShippingQuery", out);
    }
    answerPreCheckoutQuery(preCheckoutQueryId, ok, form = {}) {
        return this._form("answerPreCheckoutQuery", {
            ...form,
            pre_checkout_query_id: preCheckoutQueryId,
            ok,
        });
    }
    // --- Telegram Passport ------------------------------------------------
    setPassportDataErrors(userId, errors, form = {}) {
        return this._form("setPassportDataErrors", {
            ...form,
            user_id: userId,
            errors,
        });
    }
    // --- Telegram Stars --------------------------------------------------
    getMyStarBalance(form = {}) {
        return this._form("getMyStarBalance", form);
    }
    getStarTransactions(form = {}) {
        return this._form("getStarTransactions", form);
    }
    refundStarPayment(userId, telegramPaymentChargeId, form = {}) {
        return this._form("refundStarPayment", {
            ...form,
            user_id: userId,
            telegram_payment_charge_id: telegramPaymentChargeId,
        });
    }
    editUserStarSubscription(userId, telegramPaymentChargeId, isCanceled, form = {}) {
        return this._form("editUserStarSubscription", {
            ...form,
            user_id: userId,
            telegram_payment_charge_id: telegramPaymentChargeId,
            is_canceled: isCanceled,
        });
    }
    // --- Games -----------------------------------------------------------
    sendGame(chatId, gameShortName, form = {}) {
        return this._form("sendGame", {
            ...form,
            chat_id: chatId,
            game_short_name: gameShortName,
        });
    }
    setGameScore(userId, score, form = {}) {
        return this._form("setGameScore", {
            ...form,
            user_id: userId,
            score,
        });
    }
    getGameHighScores(userId, form = {}) {
        return this._form("getGameHighScores", {
            ...form,
            user_id: userId,
        });
    }
    // --- Delete messages ------------------------------------------------
    deleteMessage(chatId, messageId, form = {}) {
        return this._form("deleteMessage", {
            ...form,
            chat_id: chatId,
            message_id: messageId,
        });
    }
    deleteMessages(chatId, messageIds, form = {}) {
        return this._form("deleteMessages", {
            ...form,
            chat_id: chatId,
            message_ids: stringify(messageIds),
        });
    }
    deleteMessageReaction(chatId, messageId, form = {}) {
        return this._form("deleteMessageReaction", {
            ...form,
            chat_id: chatId,
            message_id: messageId,
        });
    }
    deleteAllMessageReactions(chatId, form = {}) {
        return this._form("deleteAllMessageReactions", {
            ...form,
            chat_id: chatId,
        });
    }
    // --- Gifts -----------------------------------------------------------
    getAvailableGifts(form = {}) {
        return this._form("getAvailableGifts", form);
    }
    sendGift(giftId, form = {}) {
        return this._form("sendGift", {
            ...form,
            gift_id: giftId,
        });
    }
    giftPremiumSubscription(userId, monthCount, starCount, form = {}) {
        return this._form("giftPremiumSubscription", {
            ...form,
            user_id: userId,
            month_count: monthCount,
            star_count: starCount,
        });
    }
    // --- Verification ---------------------------------------------------
    verifyUser(userId, form = {}) {
        return this._form("verifyUser", {
            ...form,
            user_id: userId,
        });
    }
    verifyChat(chatId, form = {}) {
        return this._form("verifyChat", {
            ...form,
            chat_id: chatId,
        });
    }
    removeUserVerification(userId, form = {}) {
        return this._form("removeUserVerification", {
            ...form,
            user_id: userId,
        });
    }
    removeChatVerification(chatId, form = {}) {
        return this._form("removeChatVerification", {
            ...form,
            chat_id: chatId,
        });
    }
    // --- Business accounts ----------------------------------------------
    readBusinessMessage(businessConnectionId, chatId, messageId, form = {}) {
        return this._form("readBusinessMessage", {
            ...form,
            business_connection_id: businessConnectionId,
            chat_id: chatId,
            message_id: messageId,
        });
    }
    deleteBusinessMessages(businessConnectionId, messageIds, form = {}) {
        return this._form("deleteBusinessMessages", {
            ...form,
            business_connection_id: businessConnectionId,
            message_ids: messageIds,
        });
    }
    setBusinessAccountName(businessConnectionId, firstName, form = {}) {
        return this._form("setBusinessAccountName", {
            ...form,
            business_connection_id: businessConnectionId,
            first_name: firstName,
        });
    }
    setBusinessAccountUsername(businessConnectionId, form = {}) {
        return this._form("setBusinessAccountUsername", {
            ...form,
            business_connection_id: businessConnectionId,
        });
    }
    setBusinessAccountBio(businessConnectionId, form = {}) {
        return this._form("setBusinessAccountBio", {
            ...form,
            business_connection_id: businessConnectionId,
        });
    }
    async setBusinessAccountProfilePhoto(businessConnectionId, photo, form = {}) {
        const fieldName = photo.type === "static" ? "photo" : "animation";
        const fileInput = photo[fieldName];
        const { file } = await prepareFile(fileInput, {}, this.options.filepath);
        if (!file) {
            throw new FatalError("setBusinessAccountProfilePhoto only supports file uploads (Buffer, Stream, or local path)");
        }
        const struct = { ...photo, [fieldName]: `attach://${fieldName}` };
        return this._request("setBusinessAccountProfilePhoto", {
            form: { ...form, photo: stringify(struct), business_connection_id: businessConnectionId },
            formData: { [fieldName]: file },
        });
    }
    removeBusinessAccountProfilePhoto(businessConnectionId, form = {}) {
        return this._form("removeBusinessAccountProfilePhoto", {
            ...form,
            business_connection_id: businessConnectionId,
        });
    }
    setBusinessAccountGiftSettings(businessConnectionId, showGiftButton, acceptedGiftTypes, form = {}) {
        return this._form("setBusinessAccountGiftSettings", {
            ...form,
            business_connection_id: businessConnectionId,
            show_gift_button: showGiftButton,
            accepted_gift_types: acceptedGiftTypes,
        });
    }
    getBusinessAccountStarBalance(businessConnectionId, form = {}) {
        return this._form("getBusinessAccountStarBalance", {
            ...form,
            business_connection_id: businessConnectionId,
        });
    }
    transferBusinessAccountStars(businessConnectionId, starCount, form = {}) {
        return this._form("transferBusinessAccountStars", {
            ...form,
            business_connection_id: businessConnectionId,
            star_count: starCount,
        });
    }
    getBusinessAccountGifts(businessConnectionId, form = {}) {
        return this._form("getBusinessAccountGifts", {
            ...form,
            business_connection_id: businessConnectionId,
        });
    }
    getUserGifts(userId, form = {}) {
        return this._form("getUserGifts", {
            ...form,
            user_id: userId,
        });
    }
    getChatGifts(chatId, form = {}) {
        return this._form("getChatGifts", {
            ...form,
            chat_id: chatId,
        });
    }
    convertGiftToStars(businessConnectionId, ownedGiftId, form = {}) {
        return this._form("convertGiftToStars", {
            ...form,
            business_connection_id: businessConnectionId,
            owned_gift_id: ownedGiftId,
        });
    }
    upgradeGift(businessConnectionId, ownedGiftId, form = {}) {
        return this._form("upgradeGift", {
            ...form,
            business_connection_id: businessConnectionId,
            owned_gift_id: ownedGiftId,
        });
    }
    transferGift(businessConnectionId, ownedGiftId, newOwnerChatId, form = {}) {
        return this._form("transferGift", {
            ...form,
            business_connection_id: businessConnectionId,
            owned_gift_id: ownedGiftId,
            new_owner_chat_id: newOwnerChatId,
        });
    }
    // --- Stories --------------------------------------------------------
    async postStory(businessConnectionId, content, activePeriod, options = {}) {
        if (!content.type)
            throw new FatalError("content.type is required");
        const form = {
            ...options,
            business_connection_id: businessConnectionId,
            active_period: activePeriod,
        };
        const fileInput = content[content.type];
        const { formData, fileIds } = await prepareFiles(content.type, [{ media: fileInput }], {}, this.options.filepath);
        const inputContent = { ...content };
        inputContent[content.type] = fileIds[0] ?? `attach://${content.type}_0`;
        form.content = stringify(inputContent);
        return this._request("postStory", { form, formData });
    }
    repostStory(businessConnectionId, fromChatId, fromStoryId, activePeriod, form = {}) {
        return this._form("repostStory", {
            ...form,
            business_connection_id: businessConnectionId,
            from_chat_id: fromChatId,
            from_story_id: fromStoryId,
            active_period: activePeriod,
        });
    }
    async editStory(businessConnectionId, storyId, content, options = {}) {
        if (!content.type)
            throw new FatalError("content.type is required");
        const form = {
            ...options,
            business_connection_id: businessConnectionId,
            story_id: storyId,
        };
        const fileInput = content[content.type];
        const { formData, fileIds } = await prepareFiles(content.type, [{ media: fileInput }], {}, this.options.filepath);
        const inputContent = { ...content };
        inputContent[content.type] = fileIds[0] ?? `attach://${content.type}_0`;
        form.content = stringify(inputContent);
        return this._request("editStory", { form, formData });
    }
    deleteStory(businessConnectionId, storyId, form = {}) {
        return this._form("deleteStory", {
            ...form,
            business_connection_id: businessConnectionId,
            story_id: storyId,
        });
    }
}
export default TelegramBot;
//# sourceMappingURL=telegram.js.map