import { type ApiMethods as ApiMethodsF, type InputMedia as InputMediaF, type InputMediaAnimation as InputMediaAnimationF, type InputMediaAudio as InputMediaAudioF, type InputMediaDocument as InputMediaDocumentF, type InputMediaPhoto as InputMediaPhotoF, type InputMediaVideo as InputMediaVideoF, type InputSticker as InputStickerF, type Opts as OptsF,} from "@grammyjs/types";import { createReadStream, type ReadStream } from "fs";import fetch from "node-fetch";import { basename } from "path";import { debug as d } from "./platform.node";
const debug = d("grammy:warn");
export * from "@grammyjs/types";
type MaybeSupplier<T> = T | (() => T | Promise<T>);interface URLLike { url: string;}
export class InputFile { private consumed = false; private readonly fileData: ConstructorParameters<typeof InputFile>[0]; public readonly filename?: string; constructor( file: MaybeSupplier< | string | URL | URLLike | Uint8Array | ReadStream | Iterable<Uint8Array> | AsyncIterable<Uint8Array> >, filename?: string, ) { this.fileData = file; filename ??= this.guessFilename(file); this.filename = filename; if ( typeof file === "string" && (file.startsWith("http:") || file.startsWith("https:")) ) { debug( `InputFile received the local file path '${file}' that looks like a URL. Is this a mistake?`, ); } } private guessFilename( file: ConstructorParameters<typeof InputFile>[0], ): string | undefined { if (typeof file === "string") return basename(file); if ("url" in file) return basename(file.url); if (!(file instanceof URL)) return undefined; if (file.pathname !== "/") { const filename = basename(file.pathname); if (filename) return filename; } return basename(file.hostname); } async toRaw(): Promise< Uint8Array | Iterable<Uint8Array> | AsyncIterable<Uint8Array> > { if (this.consumed) { throw new Error("Cannot reuse InputFile data source!"); } const data = this.fileData; if (typeof data === "string") return createReadStream(data); if (data instanceof URL) { return data.protocol === "file" ? createReadStream(data.pathname) : fetchFile(data); } if ("url" in data) return fetchFile(data.url); if (data instanceof Uint8Array) return data; if (typeof data === "function") { return new InputFile(await data()).toRaw(); } this.consumed = true; return data; }}
async function* fetchFile(url: string | URL): AsyncIterable<Uint8Array> { const { body } = await fetch(url); for await (const chunk of body) { if (typeof chunk === "string") { throw new Error( `Could not transfer file, received string data instead of bytes from '${url}'`, ); } yield chunk; }}
export type ApiMethods = ApiMethodsF<InputFile>;
export type Opts<M extends keyof ApiMethods> = OptsF<InputFile>[M];
export type InputSticker = InputStickerF<InputFile>;
export type InputMedia = InputMediaF<InputFile>;export type InputMediaPhoto = InputMediaPhotoF<InputFile>;export type InputMediaVideo = InputMediaVideoF<InputFile>;export type InputMediaAnimation = InputMediaAnimationF<InputFile>;export type InputMediaAudio = InputMediaAudioF<InputFile>;export type InputMediaDocument = InputMediaDocumentF<InputFile>;