@yaebal/file-id
parse, inspect and re-serialize telegram file_id and file_unique_id strings. a file_id isn't opaque — it's a TL-serialized TDLib blob (behind zero-byte RLE and url-safe base64) carrying the file's datacenter, type, access hash, file reference and photo-size source. zero dependencies, pure js, runs anywhere — not a plugin, just a parser.
install
pnpm add @yaebal/file-idusage
import { FileId, FileType, isPhotoFileId } from "@yaebal/file-id";
const file = FileId.from(ctx.document.file_id);
file.kind // "photo" | "document" | "web"
file.fileType // FileType.Sticker, FileType.Video, …
file.dcId // 1..5 — which telegram datacenter stores the file
file.accessHash // bigint
file.hasReference // carries a fresh file_reference?
// full discriminated union for narrowing
if (isPhotoFileId(file.raw)) {
file.raw.photoSize; // legacy | thumbnail | dialog_photo_* | sticker_set_thumbnail*
}
file.toString(); // re-serializes — round-trips the original string byte-for-bytededupe with file_unique_id
// file_id is bot-scoped and may rotate; file_unique_id is the stable identity.
// it's derivable from the full id — same unique id ⇒ same underlying file:
const uniqueKey = FileId.from(msg.document.file_id).toUniqueId().toString();
// or parse one you already have:
import { FileUniqueId } from "@yaebal/file-id";
const unique = FileUniqueId.from(msg.document.file_unique_id);
unique.kind; // "photo" | "document" | "web" | "secure" | "encrypted" | "temp"example: which datacenter?
forwards keep the original upload's file_id internals, so this works on forwarded media too.
import { FileId } from "@yaebal/file-id";
const DC_NAMES: Record<number, string> = {
1: "Miami, FL, USA",
2: "Amsterdam, NL",
3: "Miami, FL, USA",
4: "Amsterdam, NL",
5: "Singapore",
};
bot.on("message:photo", (ctx) => {
const file = FileId.from(ctx.photo.at(-1)!.file_id);
return ctx.reply(`stored on DC ${file.dcId} (${DC_NAMES[file.dcId] ?? "?"})`);
});api
| export | kind | description |
|---|---|---|
FileId | class | FileId.from(string) parses; getters for the common fields; .raw is the discriminated union; .toString() re-serializes; .toUniqueId() derives the dedupe key. |
FileUniqueId | class | the same treatment for file_unique_id strings. |
parseFileId / serializeFileId / parseFileUniqueId / serializeFileUniqueId / fileUniqueIdFromFileId | functions | the functional api — every class entry point without the wrapper. |
isPhotoFileId, isDocumentFileId, isWebFileId, isStickerFileId, … | guards | type-narrowing predicates for .raw (file ids, unique ids and photo-size sources). |
FileType, PhotoSizeSourceType, FileUniqueType | constants | TDLib-mirroring tag spaces (FileType.Sticker === 8, …). |
base64urlEncode/Decode, rleEncode/Decode, packTlString/unpackTlString, BinaryReader, BinaryWriter | low-level | the encoding primitives the parser is built on — for custom TDLib-flavored formats. |
errors
FileIdParseError— malformed input (bad base64url, truncated payload, unknown tags). carries.input.UnsupportedFileIdVersionError— telegram bumped the format past what this parser knows (.version/.subVersion). open an issue when you hit it.
notes
- parse → serialize round-trips the original string byte-for-byte — safe to store the parsed form.
accessHashand ids arebigints; serialize them yourself beforeJSON.stringify.- this package reads ids; it never talks to telegram. downloading is @yaebal/files.
related
- @yaebal/files — resolve and download the file behind an id.
- @yaebal/media-cache — cache
file_ids to skip re-uploads.