@gramio/files
Version:
Set of utils to work with files uploading to Telegram Bot API
285 lines (278 loc) • 12.6 kB
TypeScript
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 };