Skip to main content

Transcribe Telegram voice & video messages locally with Whisper, merged into one chat transcript

Project description

whispergram

CI PyPI version PyPI downloads Python License Platform Offline Round-trip Last commit GitHub issues

Telegram voice-to-text, locally. Transcribe Telegram voice and round video messages with Whisper (faster-whisper) and merge them into one searchable, LLM-ready chat transcript โ€” 100% offline, no API key, no cloud.

Every line is tagged by sender and timestamp, with voice notes transcribed inline next to the text:

[2026-06-20 12:33] Alex (voice 14s): hey, just finished the thing we talked about
[2026-06-20 12:35] You: nice, send it over
[2026-06-20 12:46] Alex (video-note 8s): here it is
[2026-06-20 12:47] You (photo): looks great
[2026-06-20 12:47] Alex (sticker ๐Ÿ‘)

Why?

An audio-heavy Telegram chat is unreadable and unsearchable โ€” you cannot grep a voice note, and you cannot hand a folder of .ogg files to an LLM. The alternatives are worse: Telegram Premium transcribes one message at a time by hand, and cloud speech APIs upload your private audio to a third party. whispergram transcribes every voice and video note in one pass, entirely on your own machine, and weaves them back into the text timeline as a single file you can read, search, or feed to a model.


Features

Feature Description
Voice + video notes Both voice_message and round video_message notes are transcribed inline with the text
One merged file A single merged_chat.md, chronological, every line tagged [time] sender
100% local & offline faster-whisper runs on your machine โ€” no upload, no API key, no account
Lossless mapping Stickers, photos, animations, documents, music, locations, polls and contacts appear as markers โ€” nothing content-bearing is dropped
Handles missing media Notes excluded from the export are clearly marked [not exported], never fed to the model
All text shapes Reconstructs plain, rich, and entity-based message text (links, mentions, custom emoji)
Dry-run Preview the full merge with --dry-run โ€” no model download, no GPU, instant
GPU or CPU CUDA with automatic CPU fallback; a one-command Windows CUDA fix is built in
Auto-detect Finds the export JSON (any filename) and the language per file
Tested 44 offline tests on the Python 3.9โ€“3.13 CI matrix

Quick Start

1. Install

Via PyPI (recommended):

pip install whispergram

Or clone for development:

git clone https://github.com/davidmalko87/whispergram.git
cd whispergram
pip install -r requirements.txt

You also need ffmpeg on your PATH:

# Linux:  sudo apt install ffmpeg
# macOS:  brew install ffmpeg
# Windows: choco install ffmpeg   (or: winget install Gyan.FFmpeg)

2. Export your chat from Telegram

Telegram Desktop โ†’ open the chat โ†’ โ‹ฎ menu โ†’ Export chat history:

  • Format: JSON
  • Tick Voice messages (and Video messages for round notes)

You get a folder with a .json file plus voice_messages/ and video_files/ subfolders.

3. Run

From inside the export folder:

whispergram
# or, without installing:
python whispergram.py

โ€ฆor point it at the folder:

whispergram "path/to/ChatExport_2026-06-20"

The result is merged_chat.md in the export folder.


Example output

[2026-06-20 12:33] Alex: did you get the files?
[2026-06-20 12:33] You: yep, check https://example.com thanks
[2026-06-20 12:34] Alex (voice 6s): one sec, recording the summary now ...
[2026-06-20 12:35] Alex (video-note 8s): [not exported]
[2026-06-20 12:35] You (sticker ๐Ÿ˜…)
[2026-06-20 12:36] Alex (photo): the whiteboard from today

How each message appears

Message type In the merged file
Text [time] sender: message text
Voice note [time] sender (voice 12s): <transcript>
Round video note [time] sender (video-note 8s): <transcript>
Voice/video note with caption [time] sender (voice 12s): <transcript> | caption: <text>
Voice/video not downloaded [time] sender (voice 12s): [not exported]
Sticker [time] sender (sticker ๐Ÿ˜…)
Photo (with caption) [time] sender (photo): caption
Animation / GIF [time] sender (animation)
Document [time] sender (file: report.pdf): caption
Location / poll / contact [time] sender (location) ยท (poll) ยท (contact)
Music / audio file [time] sender (audio: Artist - Title) โ€” transcribe with --audio-files

Markers can be turned off with --no-media-markers (voice/video notes are always transcribed).


โœ… Round-trip Validated

The merge has been validated against a real 770-message Telegram export (a live, audio-heavy chat โ€” not a synthetic fixture). Every dimension was diffed against the source JSON:

Dimension In export In merged file Result
Voice notes (downloaded) 4 4 transcribed โœ…
Round video notes (not downloaded) 5 5 [not exported] โœ…
Other media (stickers, photos, animations, videos, audio, โ€ฆ) 107 107 markers โœ…
Text messages 654 654 โœ…
Messages dropped โ€” 0 โœ…

All 770 messages map to 770 lines โ€” the per-type counts match the source exactly, and not-exported notes are never sent to the model. (An earlier version silently dropped 88 of those messages โ€” every sticker, photo, and caption-less media item โ€” leaving misleading gaps. The round-trip is what surfaced it.)

That export is private, so these counts were measured locally and are not reproducible from this repo. The synthetic export under tests/fixtures/ reproduces the same lossless mapping across every media type and guards it automatically in CI. A faithful merge is only proven once it has been run end-to-end and the output diffed back against every message type โ€” structural validity alone is not enough.


Known Limitations

These follow from the Telegram export format and from speech recognition itself โ€” not from a lack of effort in the tool:

Area Status Notes
Round video notes Audio only, if downloaded Telegram often excludes the binary; those show [not exported]
Music / audio_file Off by default Opt in with --audio-files; songs are otherwise not run through ASR
Speaker labels Sender only Each note is attributed to its Telegram sender; no in-audio diarization
Timestamps Minute resolution Telegram exports YYYY-MM-DDThh:mm; seconds are not shown
Reactions / edits / replies Not represented The merged file is a clean reading transcript, not a full forensic dump
Transcription accuracy Model-dependent large-v3 is best for uk/ru; --lang forces a language if auto-detect slips

Options

whispergram --device cpu --model large-v3-turbo   # no GPU, fast
whispergram --lang uk                             # force a language
whispergram --dry-run                             # preview the merge, no transcription
whispergram --audio-files                         # also transcribe music/long audio files
whispergram --out result.md                       # custom output path
Flag Default Notes
--device cuda cuda or cpu; auto-falls back to CPU if the GPU fails
--model large-v3 try large-v3-turbo or medium if CPU is slow
--lang auto force a code like uk, ru, en if auto-detect mislabels
--out merged_chat.md output file
--audio-files off also transcribe audio_file messages (music, long memos)
--no-media-markers off omit (sticker) / (photo) / (file) markers
--dry-run off map the chat without loading a model or transcribing
--setup-cuda-windows โ€” copy CUDA DLLs next to ctranslate2, then exit (Windows GPU fix)

GPU (CUDA) setup

Linux / macOS: with a working CUDA install it runs as-is on --device cuda.

Windows โ€” the common pitfall is RuntimeError: Library cublas64_12.dll is not found:

  1. Install the CUDA runtime wheels (no full CUDA Toolkit needed):
    pip install nvidia-cublas-cu12 nvidia-cudnn-cu12
    pip install -U "ctranslate2>=4.5"
    
  2. If it still can't find the DLL, copy them next to CTranslate2 (the reliable fix):
    python whispergram.py --setup-cuda-windows
    
  3. Or skip the GPU entirely: --device cpu --model large-v3-turbo.

CTranslate2 loads cuBLAS/cuDNN lazily in native code that ignores os.add_dll_directory, which is why placing the DLLs inside the package dir is the dependable solution.


FAQ

How do I transcribe Telegram voice messages? Export the chat from Telegram Desktop as JSON (with voice messages), then run whispergram in the export folder. Every voice note is transcribed with Whisper and merged into the text chat.

Is it private / offline? Does my audio leave my machine? Yes, it is offline. Transcription runs locally with faster-whisper and needs no account or API key. The tool makes no network calls with your data; faster-whisper downloads the speech model once on first run, then works fully offline. Your chat audio and transcripts never leave your machine.

Do I need a GPU? No. It runs on CPU (--device cpu); use --model large-v3-turbo for speed. A CUDA GPU is faster.

Does it handle round video messages / video notes? Yes โ€” round video_message notes are transcribed from their audio, just like voice notes.

Which languages work? Any language Whisper supports. large-v3 handles Ukrainian and Russian well; use --lang uk (or ru, en, โ€ฆ) to force one if auto-detection slips.

How is this different from Telegram Premium's transcription? Premium transcribes one message at a time, by hand, in the app. whispergram transcribes the entire chat in one pass, offline, and produces a single searchable file.


Project Structure

whispergram/
โ”œโ”€โ”€ whispergram.py             # The tool: text reconstruction, mapping, transcription, CLI
โ”œโ”€โ”€ requirements.txt           # Runtime dependency (faster-whisper)
โ”œโ”€โ”€ pyproject.toml             # Packaging + ruff + pytest configuration
โ”œโ”€โ”€ CHANGELOG.md
โ”œโ”€โ”€ CONTRIBUTING.md
โ”œโ”€โ”€ LICENSE
โ”œโ”€โ”€ README.md
โ”‚
โ”œโ”€โ”€ .github/
โ”‚   โ”œโ”€โ”€ workflows/
โ”‚   โ”‚   โ”œโ”€โ”€ ci.yml             # ruff + pytest on Python 3.9โ€“3.13 (no transcription deps)
โ”‚   โ”‚   โ””โ”€โ”€ publish.yml        # tag v* โ†’ verify version โ†’ build โ†’ PyPI (trusted publishing)
โ”‚   โ”œโ”€โ”€ ISSUE_TEMPLATE/
โ”‚   โ””โ”€โ”€ dependabot.yml
โ”‚
โ””โ”€โ”€ tests/
    โ”œโ”€โ”€ test_whispergram.py    # 44 offline tests โ€” no model download or GPU required
    โ””โ”€โ”€ fixtures/
        โ””โ”€โ”€ sample_export/
            โ””โ”€โ”€ result.json    # synthetic export (safe to commit; used by tests + CI)

โš ๏ธ Privacy

This tool processes private conversations, and the transcripts it produces are just as sensitive as the audio. Two rules:

  • Nothing leaves your machine. Transcription is fully local; the tool makes no network calls with your data and needs no credentials.
  • Never commit your exports or transcripts. The included .gitignore blocks chat data (*.json, audio files, merged_chat.md, ChatExport_*/) by default โ€” keep it. Build your repo in a folder separate from any export, keep any --out path inside the export folder, and run git status before pushing to confirm only code is staged. The only data file in this repo is the synthetic fixture under tests/fixtures/.

Requirements

  • Python 3.9+
  • ffmpeg on your PATH
  • faster-whisper >= 1.0 (pip install -r requirements.txt)
  • For NVIDIA GPU on Windows: nvidia-cublas-cu12, nvidia-cudnn-cu12, ctranslate2>=4.5

The test suite needs none of the above โ€” only ruff and pytest.


Changelog

See CHANGELOG.md for the full version history.

Contributing

See CONTRIBUTING.md for the development setup, the privacy rule, and the versioning / release policy.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

whispergram-0.1.0.tar.gz (20.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

whispergram-0.1.0-py3-none-any.whl (13.0 kB view details)

Uploaded Python 3

File details

Details for the file whispergram-0.1.0.tar.gz.

File metadata

  • Download URL: whispergram-0.1.0.tar.gz
  • Upload date:
  • Size: 20.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for whispergram-0.1.0.tar.gz
Algorithm Hash digest
SHA256 5d6de09d08b95be6b06c9abf6b29b4db19910754d14c23a1003eac2038d55985
MD5 f81278e92e21c125a66f0d9da09e4bfd
BLAKE2b-256 bf9affbb503372353f1ed5be366330255328bacd92389d3e76bae2eac3a8177e

See more details on using hashes here.

Provenance

The following attestation bundles were made for whispergram-0.1.0.tar.gz:

Publisher: publish.yml on davidmalko87/whispergram

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file whispergram-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: whispergram-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 13.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for whispergram-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 17205c1888edf6a4c64a91e07a0c7e0130fee50796833bf841ba615bb0854b3c
MD5 ac30562fcc4a6a0b933812c5ca25268b
BLAKE2b-256 9f72b24a34db982a82619b0855cafc97dd870d434415dad6ad379d57ad462234

See more details on using hashes here.

Provenance

The following attestation bundles were made for whispergram-0.1.0-py3-none-any.whl:

Publisher: publish.yml on davidmalko87/whispergram

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page