UNPKG

@gramio/files

Version:

Set of utils to work with files uploading to Telegram Bot API

285 lines (278 loc) 12.6 kB
import { Readable } from 'node:stream'; import { APIMethods, APIMethodParams, TelegramInputFile, TelegramInputMediaAnimation, TelegramInputMediaDocument, TelegramInputMediaAudio, TelegramInputMediaPhoto, TelegramInputMediaVideo, TelegramFile } from '@gramio/types'; /** Guard to check is method used for File Uploading */ declare function isMediaUpload<T extends keyof APIMethods>(method: T, params: NonNullable<APIMethodParams<T>>): boolean; /** * Helper to convert JSON to FormData that can accept Telegram Bot API. * if File is not top-level property it will be `“attach://<file_attach_name>”` * * [Documentation](https://core.telegram.org/bots/api#inputfile) */ declare function convertJsonToFormData<T extends keyof APIMethods>(method: T, params: NonNullable<APIMethodParams<T>>): Promise<FormData>; /** * Helper to extract files from params and convert them to FormData. (Similar to {@link convertJsonToFormData}) * if File is not top-level property it will be `“attach://<file_attach_name>”` * * [Documentation](https://core.telegram.org/bots/api#inputfile) */ declare function extractFilesToFormData<T extends keyof APIMethods>(method: T, params: NonNullable<APIMethodParams<T>>): Promise<[FormData | undefined, NonNullable<APIMethodParams<T>>]>; /** Helper for convert Readable stream to buffer */ declare function convertStreamToBuffer(stream: Readable): Promise<Buffer>; type Extractor = { name: string; type: "array" | "union"; property: string; }; type MethodsWithMediaUpload = { [Method in keyof APIMethods]?: [ (params: NonNullable<APIMethodParams<Method>>) => boolean, Extractor[] | null ]; }; /** Guard to check is it {@link Blob} or {@link Promise} */ declare function isBlob(blob?: TelegramInputFile | object | string): boolean; /** * A set of methods with the function of checking whether a {@link File} has been passed in the parameters * * @codegenerated * */ declare const MEDIA_METHODS: MethodsWithMediaUpload; /** * Class-helper with static methods that represents the content of a media message to be sent. * * [Documentation](https://gramio.dev/files/media-input.html) */ declare class MediaInput { /** * Represents an animation file (GIF or H.264/MPEG-4 AVC video without sound) to be sent. * * [Documentation](https://core.telegram.org/bots/api/#inputmediaanimation) */ static animation(media: TelegramInputMediaAnimation["media"], options?: Omit<TelegramInputMediaAnimation, "media" | "type">): TelegramInputMediaAnimation; /** * Represents a general file to be sent. * * [Documentation](https://core.telegram.org/bots/api/#inputmediadocument) */ static document(media: TelegramInputMediaDocument["media"], options?: Omit<TelegramInputMediaDocument, "media" | "type">): TelegramInputMediaDocument; /** * Represents an audio file to be treated as music to be sent. * * [Documentation](https://core.telegram.org/bots/api/#inputmediaaudio) */ static audio(media: TelegramInputMediaAudio["media"], options?: Omit<TelegramInputMediaAudio, "media" | "type">): TelegramInputMediaAudio; /** * Represents a photo to be sent. * * [Documentation](https://core.telegram.org/bots/api/#inputmediaphoto) */ static photo(media: TelegramInputMediaPhoto["media"], options?: Omit<TelegramInputMediaPhoto, "media" | "type">): TelegramInputMediaPhoto; /** * Represents a video to be sent. * * [Documentation](https://core.telegram.org/bots/api/#inputmediavideo) */ static video(media: TelegramInputMediaVideo["media"], options?: Omit<TelegramInputMediaVideo, "media" | "type">): TelegramInputMediaVideo; } /** * Class-helper with static methods for file uploading. * * [Documentation](https://gramio.dev/files/media-upload.html) */ declare class MediaUpload { /** * Method for uploading Media File by local path. */ static path(path: string, filename?: string): Promise<File>; /** * Reference a file by its **local path on a self-hosted Bot API server** * (`--local` mode) using the `file://` URI scheme. The server reads the file * straight from its own disk, so the bytes are **never transferred over HTTP**. * * This is an **optimization**, not a requirement for large files: a local Bot * API server already accepts up to **2 GB** via a normal upload * ({@link MediaUpload.path} / {@link MediaUpload.stream}). Use `localPath` only * when the file already lives **on the Bot API server's filesystem** (bot * co-located, or a shared volume) to skip re-uploading it. * * @example * ```ts * // file already on the server's disk → no HTTP upload of the bytes * ctx.sendDocument(MediaUpload.localPath("/var/data/big-archive.zip")); * ``` */ static localPath(path: string): string; /** * Method for uploading Media File by Readable stream. */ static stream(stream: Readable | ReadableStream, filename?: string): Promise<File>; /** * Method for uploading Media File by BinaryLike (Buffer or ArrayBuffer and etc). */ static buffer(buffer: Exclude<BufferSource | ArrayBuffer, string>, filename?: string): File; /** * Method for uploading Media File by URL (also with fetch options). */ static url(url: URL | string, filename?: string, options?: RequestInit): Promise<File>; /** * Method for uploading Media File by text content. */ static text(text: string, filename?: string): File; } /** A resolved file location used by {@link TelegramFileDownload}. */ type FileDownloadSource = { type: "url"; url: string; } | { type: "disk"; path: string; } | { type: "data"; data: ArrayBuffer | Uint8Array; }; /** Lazily resolves the file metadata + where to read it from. */ type FileDownloadResolver = () => Promise<{ file: TelegramFile; source: FileDownloadSource; }>; /** * Lazy, `Response`-like handle for downloading a Telegram file. * * It is a `PromiseLike<ArrayBuffer>` — so `await download` still yields the bytes, * exactly like before — **plus** convenience readers, mirroring the web `Response`: * * ```ts * const dl = ctx.download(); // nothing fetched yet * await dl.bytes(); // Uint8Array * await dl.text(); // string (utf-8) * await dl.json(); // parsed JSON * await dl.blob(); // Blob * await dl.stream(); // ReadableStream (pipe huge files) * await dl.toFile("./photo.jpg"); // save to disk, returns the path * await dl.info(); // getFile metadata (no body read) * const buf = await ctx.download(); // ArrayBuffer (back-compatible) * ``` * * Like `Response`, the body is **single-use**: read it once, then call * `download()` again for another pass. `info()` and `toFile()` (for a local * server file) don't consume the body. */ declare class TelegramFileDownload implements PromiseLike<ArrayBuffer> { #private; constructor(resolver: FileDownloadResolver); private resolve; private response; /** The `getFile` metadata (`file_id`, `file_path`, `file_size`, …). Does not read the body. */ info(): Promise<TelegramFile>; /** The whole file as an `ArrayBuffer`. */ arrayBuffer(): Promise<ArrayBuffer>; /** The whole file as a `Uint8Array`. */ bytes(): Promise<Uint8Array>; /** The whole file as a `Blob`. */ blob(): Promise<Blob>; /** The file decoded as UTF-8 text. */ text(): Promise<string>; /** The file parsed as JSON. */ json<T = unknown>(): Promise<T>; /** A web `ReadableStream` of the file — pipe large files without buffering. */ stream(): Promise<ReadableStream<Uint8Array>>; /** * A download URL for the file. Does not read the body. * * ⚠️ **Contains the bot token by default** — the standard Telegram URL is * `…/file/bot<token>/<path>`. It is **token-less only when `files.baseURL` is * set** (a local server serving the working dir over HTTP), where it becomes * `${files.baseURL}/<path>`. Don't hand the default (token-bearing) link to end * users; configure `files.baseURL`, or proxy downloads yourself. * * Throws for a local-server file (`--local`, absolute path) with no * `files.baseURL` — there's no HTTP URL, only a path on the server's disk. */ link(): Promise<string>; /** Save the file to `path` and return it. Copies on disk when possible (no extra transfer). */ toFile(path: string): Promise<string>; /** `PromiseLike`: `await download` resolves the file bytes (back-compatible). */ then<TResult1 = ArrayBuffer, TResult2 = never>(onfulfilled?: ((value: ArrayBuffer) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>; } /** * Strategy for how {@link downloadFile} obtains file bytes — mainly relevant with a * **local Bot API server** (`--local`), where `getFile` returns an absolute path on * the server's disk instead of a URL. * * - `"auto"` *(default)* — absolute `file_path` → `baseURL` rewrite if set, else read * from disk; relative `file_path` → classic download URL. * - `"url"` — classic `<baseURL>/file/bot<token>/<path>` download. * - `"disk"` — read the absolute `file_path` straight from the local filesystem. * - `"rewrite"` — fetch `${files.baseURL}/<relative path>` over HTTP (token-less). * - a function — full custom resolution. */ type FileSource = "auto" | "url" | "disk" | "rewrite" | ((file: TelegramFile) => Promise<ArrayBuffer> | ArrayBuffer); /** * The minimal `Bot`-shaped object {@link downloadFile} needs. The GramIO `Bot` * satisfies it structurally, so you just pass the instance — and a custom * "without gramio" wrapper can pass any object of this shape. */ interface FileDownloadBot { api: { getFile(params: { file_id: string; }): Promise<TelegramFile> | TelegramFile; }; options: { token: string; api: { baseURL: string; }; files?: { /** How to fetch bytes. @default "auto" */ source?: FileSource; /** Working directory of the local server — the prefix of the absolute `file_path` it returns. @default "/var/lib/telegram-bot-api" */ localDir?: string; /** * Where that working dir is mounted **on the bot's side**, when the bot and * server share a volume at a *different* path. For `source: "disk"`, the * server path is remapped `localDir` → `mountDir` before reading. Defaults to * `localDir` (same path — no remap). * * @example * // server mounts the volume at /var/lib/telegram-bot-api, bot at /data * { localDir: "/var/lib/telegram-bot-api", mountDir: "/data" } */ mountDir?: string; /** Public base URL the working dir is served at (enables token-less links + `source: "rewrite"`). */ baseURL?: string; }; }; } /** Anything {@link downloadFile} can turn into a `file_id`. */ type DownloadFileInput = string | { file_id: string; } | { fileId: string; } | { bigSize: { fileId: string; }; }; /** * Download a Telegram file. Returns a {@link TelegramFileDownload} handle * (`.bytes()`, `.text()`, `.json()`, `.blob()`, `.stream()`, `.toFile()`, * `.link()`, `.info()`; `await` it for an `ArrayBuffer`). Pass `path` to save * straight to disk and get the path back. * * Powers `bot.downloadFile` / `context.download`, and is re-exported from `gramio` * so you can call it with a bot instance from anywhere. * * @example * ```ts * import { downloadFile } from "gramio"; * * const dl = downloadFile(bot, fileId); * await dl.toFile("./photo.jpg"); * const text = await downloadFile(bot, fileId).text(); * ``` */ declare function downloadFile(bot: FileDownloadBot, attachment: DownloadFileInput): TelegramFileDownload; declare function downloadFile(bot: FileDownloadBot, attachment: DownloadFileInput, path: string): Promise<string>; export { MEDIA_METHODS, MediaInput, MediaUpload, TelegramFileDownload, convertJsonToFormData, convertStreamToBuffer, downloadFile, extractFilesToFormData, isBlob, isMediaUpload }; export type { DownloadFileInput, FileDownloadBot, FileDownloadResolver, FileDownloadSource, FileSource };