@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

terminal
pnpm add @yaebal/file-id

usage

inspect.ts
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-byte

dedupe with file_unique_id

dedupe.ts
// 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.

dc-bot.ts
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

exportkinddescription
FileIdclassFileId.from(string) parses; getters for the common fields; .raw is the discriminated union; .toString() re-serializes; .toUniqueId() derives the dedupe key.
FileUniqueIdclassthe same treatment for file_unique_id strings.
parseFileId / serializeFileId / parseFileUniqueId / serializeFileUniqueId / fileUniqueIdFromFileIdfunctionsthe functional api — every class entry point without the wrapper.
isPhotoFileId, isDocumentFileId, isWebFileId, isStickerFileId, …guardstype-narrowing predicates for .raw (file ids, unique ids and photo-size sources).
FileType, PhotoSizeSourceType, FileUniqueTypeconstantsTDLib-mirroring tag spaces (FileType.Sticker === 8, …).
base64urlEncode/Decode, rleEncode/Decode, packTlString/unpackTlString, BinaryReader, BinaryWriterlow-levelthe 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.
  • accessHash and ids are bigints; serialize them yourself before JSON.stringify.
  • this package reads ids; it never talks to telegram. downloading is @yaebal/files.

related