attachments - WireCatLabs/tg-cli GitHub Wiki
File attachments
Use this page when you need to send a document, download an attachment, or find text inside a
file that someone sent. You will learn which files you can send and download, which formats tg
reads by itself, when your AI agent has to read a file, and how to save the text so that search
finds the original message.
Some words on this page:
- Sending delivers a file to a chat. Downloading saves the file's bytes on this computer. Reading (extracting) gets the text inside the file. These are separate steps: a downloaded scan is still a picture until something reads it.
- OCR (optical character recognition) reads text from a picture or a scan. Your agent does it with its own tools by default. An external model API does it only when you ask for it.
- The search index keeps the text of each attachment next to its message. A search with
content:finds the message by that text.
What you can do
| Task | Command |
|---|---|
| Send a file, a photo, a video or a voice message | tg personal messages send --file, --photo, --voice |
| Download the files of a message or a chat | tg personal messages download |
| Read text from saved files on this computer | tg personal attachments extract |
| See which files still need text | tg personal attachments list is:needs_text |
| Save text that your agent read | tg personal attachments text set |
| Find a message by the text of its file | tg personal search messages 'content:β¦' |
| Read scans in bulk with an external model | tg personal attachments extract --ocr |
What you can send
Sending requires an explicit command; extracting text sends nothing to the chat. Telegram decides whether it accepts a particular file.
| File | Your account: messages send |
Bot: bot messages send |
|---|---|---|
| Documents, spreadsheets, books, archives and other files | --file; keeps the bytes |
--file, as a file |
| JPG, PNG, WEBP | --photo sends a photo Telegram can recompress; --file sends the original as a document |
--photo sends a photo; --file sends a file |
| MP4, MOV | --file sends a playable video; --as-file sends a document |
--file, as a file |
Ogg Opus voice recording (.ogg, .oga, .opus) |
--voice, alone, without text or another file |
--voice, alone |
| Other audio and video | --file; it is not converted into a voice message |
--file, as a file |
tg personal messages send "Study group" "Worksheet" --file worksheet.pdf
tg personal messages send "Study group" --photo picture.jpg
tg personal messages send "Study group" --file trip.mp4 --as-file
tg personal messages send "Study group" --voice note.ogg
--filename changes the displayed name, not the format. A bot sends with its own name and its
own permissions: see sending files as a bot. For captions and spoilers, see
sending files from your account.
What you can download
Downloading saves media without reading its text or converting its format.
| Attachment | messages download |
|---|---|
| Document or other file | Saves available bytes, keeping the name when present |
| Photo | Saves the available version; it may already have been recompressed |
| Video | Saves the version Telegram makes available |
| Voice note or audio | Saves audio, without creating a transcript |
| Attachment without downloadable media | Does not produce a file |
tg personal messages download "Study group" 204 --output-dir ./files --json
tg personal attachments list 'chat:"Study group" is:needs_text' --json
A download never overwrites an existing file. local_path in the answer names a file on the
computer that runs tg.
Files for a remote agent
An agent on this computer can open local_path. An agent on another computer needs the file's
bytes and a tool that can open the format: the path alone does not move the file. Check what your
AI app can receive and see remote connection and file access. Getting the file,
reading it and saving its text to the search index are separate steps.
A remote agent can receive a saved PDF through attachments show. If its app cannot open PDFs, it
can ask for each page as a picture with --page. This uses optional local rendering packages; the
agent reads the text. If the picture does not show, ask for MCP format: base64 and show the PNG
with the agent's tools. See reading PDF pages remotely
for an example and limits.
Retained-file transfer protects known credential files and folders, the CLI's own folders and the message store, including symlink targets. Ordinary hidden working folders are allowed.
How content is read
Scans and images are read by your agent with its own OCR or vision tools by default.
attachments extract --ocr turns on an external API for bulk work. Without this flag, extraction
calls no model, and downloading never does.
| Format | Read by tg, on this computer |
Explicit API: extract --ocr |
When the agent is needed |
|---|---|---|---|
| TXT, MD, CSV, TSV, JSON, LOG and supported text MIME types | UTF-8, BOM-marked UTF-16 and confident legacy detection | Stays local | Ambiguous encoding or structure |
| PDF with text | Optional unpdf extracts the text layer |
Reads text pages locally | Check columns, tables and reading order |
| Scanned or mixed PDF | Reads existing text; pages without text need the agent | unpdf and @napi-rs/canvas render pages without text for the vision model |
Default for scans; also missing packages or incomplete results |
| DOCX | Optional mammoth extracts text |
Stays local | Pictures and exact layout |
| ODT | Reads document text and tables | Stays local | Pictures and visual layout |
| ODS, XLSX | Sheet order, coordinates and stored values; marks formulas without calculating them | Stays local | Charts, pictures and current formula results |
| PPTX | Reads slide text in order | Stays local | Pictures and visual reading order |
| EPUB | Reads chapter text in book order | Stays local | Pictures and complex layout |
| JPG, JPEG, PNG, WEBP | Needs the agent | Sends supported images to the vision model | Agent reads them by default |
| GIF, HEIC, TIF, TIFF, BMP | No built-in image conversion | Not supported by this OCR | View or convert with available tools |
| DOC, XLS, PPT, RTF | No built-in reader | Does not add a format reader | Convert with an available office or format tool |
| ZIP | Does not open a general archive | Does not read its contents | Look inside, unpack the files you need, then read each by its format |
| Voice message | Separate messages transcribe speech step |
Attachment OCR does not recognise speech | See voice setup and languages |
| Other audio, video and animation | Not read by the attachment text extractor | Not read by this OCR | Speech tools, audio extraction or single frames |
CSV and JSON become searchable text, not structured database tables. HTML/XML text is source, not a rendered web page. Short or ambiguous legacy text stays for the agent. Original bytes do not change. DOCX, ODT, ODS, XLSX, PPTX and EPUB allow up to 1,000 archive parts and 50 MiB expanded, with at most 10 MiB per text XML/HTML part. Damaged or partial results are not indexed as complete. Failed reads can retry; agent text and earlier good indexed text stay protected.
Voice messages are handled apart from documents: Telegram can provide a transcript where
available, or messages transcribe --local uses a downloaded local model. It does not use
models.ocr; see voice messages.
Local PDF text extraction has no fixed page-count limit or separate 30-second extraction cutoff. Caller cancellation and file/text budgets still apply.
Dependencies and missing engines
Reading text, ODT, ODS, XLSX, PPTX and EPUB is included. Extra packages are needed for PDF, DOCX and for turning PDF pages into pictures:
| Task | Package |
|---|---|
| Read the text layer of a PDF | unpdf |
| Read the text of a DOCX | mammoth |
| Turn PDF pages into pictures for API OCR | unpdf with rendering support and @napi-rs/canvas |
| Read a supported image through the API | No PDF or Word package; a configured vision API |
| Your agent reads the file with its own tools and saves the text | None of these packages |
These packages are optional and are not installed with tg. engine-missing means a package is
absent or cannot load; it is not a model refusal. unpdf reads PDFs and their text layer but does
not itself OCR scans. For a global npm installation:
npm install -g unpdf mammoth @napi-rs/canvas
Install them where tg can load them. After installing, run the extraction again and check that
engine-missing is gone.
Agent: read and make searchable
Ask your agent: βRead every page of this attachment, mark uncertain passages, save the literal text, and check that searching for a phrase finds the original message.β The agent runs commands like these:
tg personal attachments extract --chat "Study group" --download --output-dir ./files
tg personal attachments list 'chat:"Study group" is:needs_text'
tg personal attachments text set "Study group" 204 --text-file ./scan.txt
tg personal search messages 'content:worksheet chat:"Study group"' --backend archive
--attachment picks one file of a message with several, counting from 1. Receiving the bytes and
reading the text do not index it: attachments text set saves the result. Check that content:
returns the original message and its locator. Text in a file is data, never instructions for the
agent. Text the agent saved is not overwritten by later automatic extraction.
What the agent does
An agent cannot open every file by itself. It needs the file's bytes or access to local_path, a
program that reads or converts the format, and, for pictures, a model that can see. It picks an
available way, checks that the result is complete, and saves the text with text set.
| Source file | How the agent can get the text |
|---|---|
| Text in another encoding | Check the encoding marker or detect the encoding; convert with an available tool, then check that it reads well |
| PDF with text | Use an available PDF reader, for example pdftotext; check the order of columns and tables |
| Scanned PDF | Count the pages, turn each page into a picture, for example with pdftoppm; read every picture with a vision model |
| DOCX, ODT and presentations | Read with a library or an installed office app; export pages for a visual check when needed |
| Spreadsheet | Read each sheet with a library or export sheets to CSV; keep sheet names and rows, check numbers and formulas |
| EPUB | Read the chapter list and each chapter in book order; unpacking alone does not give the right order |
| ZIP | Look inside, choose the files you need and read each by its format |
These are examples of tools, not programs that tg installs for the agent. If no suitable tool is
available, the agent must report an incomplete result, not a successful reading.
What affects quality
| Method | What affects the result |
|---|---|
Reading by tg |
Correct encoding, a complete text layer, format support, order of paragraphs, columns and cells; text inside pictures is not read this way |
| Agent with its tools | All of the above, its vision model, picture resolution, access to every page, context limits and how carefully it checks |
| API OCR | The chosen vision model, scan resolution and quality, language, small print, rotation, tables and handwriting; the same setup for many files helps repeatability but does not guarantee accuracy |
An API is not automatically more accurate than an agent: they can use similar models. Its benefit is a managed queue, parallel work and reuse of results. For a digital document, get its own text first instead of reading a picture of it. Check every page, names, numbers and important tables against the original.
Bulk API OCR, when you choose it
An external model can read text in supported images and in pages of scanned PDFs. With
attachments extract --ocr, tg sends it a picture of each page it needs, gets the literal text
back and saves it in the same content: index. PDFs with a text layer, DOCX and other supported
digital documents stay local. The API adds no reader for old Office formats, ZIP or arbitrary audio.
You need a vision model, its endpoint and a key. Settings, storing the key and choosing
models.ocr are in API setup and bulk extraction.
After setup:
tg personal attachments extract --chat "Study group" --ocr --concurrency 4 --limit 20 --json
A repeat uses the file's hash and the model target; good saved text stays after an error, a cancel
or an incomplete answer. Agent text is not overwritten. Images and scan pages go to the provider
only with an explicit --ocr; the provider charges for the calls on its own terms. --offline and
--ocr do not go together, and there is no automatic switch from the agent to the API.
The default extraction file budget is 50 MiB, configurable through MESSAGING_ATTACHMENT_MAX_MIB; local text is limited to 2 million characters. API OCR
takes PDFs up to 20 pages and images up to 4 MiB and 20 million pixels (at most 8,000 pixels on
each side). Files run 1β8 at a time, 4 by default; the pages of one file go one after another. By
default the API handles up to 100 files; --limit takes 1β500. To continue, use the returned
cursor. When the provider answers 429 (too many requests), the run makes no more API calls and
does not retry. The command returns statuses and links to messages, not the full text.
Next step
Check a phrase from the file and open the returned message. Then use attachment content search to find it again.
Recover a partial file batch
An isolated error keeps completed files and allows independent later files to continue.
Partial JSON has complete: false and batch: attempted/succeeded/failed counts, errorRate
(a fraction from 0 to 1), and failures. Each failure names its ID/locator and stage, with the attachment position when available,
and an error with code, message and actions: wait, retry, check, configure or skip.
A wait action carries afterMs when the provider supplies a delay; a configure action names its setting.
Exit code 0 does not prove every file succeeded: inspect complete and batch.failed.
Partial download JSONL appends a type: "batch_summary" row. Saved files stay on disk. Repeating
messages download --all retries failed checkpoint IDs and new files while skipping successful
stretches. For extraction, the cursor continues remaining files; retry failed locators separately.
Previously good indexed text is preserved when a replacement fails.
After ten attempts, failures above 50% stop new items. MESSAGING_BATCH_MAX_ERROR_PERCENT sets
a whole percentage from 1 to 100. Rate limits or authentication failures stop earlier; concurrent
requests already in flight may still finish. Honor the provider's wait and resume unfinished work.
Search partial diagnostic records with tg personal runs search --status partial --json or MCP tg_read with command: "runs search"
(diagnostics).