Skip to main content

Telegram Desktop Chat-History Migration

A Python tool for reconstructing a Telegram conversation in a destination channel from a Telegram Desktop export (messages.html, messages2.html, etc.) and the media files that accompany it. Use it as a command-line tool (python -m telegram_migration) or import it as a Python library (from telegram_migration import Settings, run).

Scope: this program migrates a local export. It does not sign into Telegram Web or retrieve the complete live history of an unrelated channel. Most reconstructed messages are newly sent by your destination bot; the program cannot recreate arbitrary historical sender identities or original native reaction totals. This program uses Telegram's official Bot API/MTProto bot authentication to send migrated messages, rather than logging in through your personal Telegram account. It does not require your personal phone number, SMS login code, or personal account session, reducing the exposure of your personal account credentials. The bot operates under its own identity, so sending limits and restrictions primarily affect the bot.

Features

  • Streams Telegram Desktop HTML exports instead of loading every page into memory at once.
  • Reconstructs supported rich-text formatting (including italics, bold, underline, strike, code, links, spoilers, and blockquotes) using Telegram message entities.
  • Preserves sender names and original timestamps as archival text where available.
  • Handles common exported media: photos, documents, videos, GIFs, voice messages, round videos, and sticker files.
  • Maps source message IDs to destination IDs to restore reply links where possible; audits available reply links.
  • Persists partial-send state so interrupted runs can often resume without intentionally resending already-recorded pieces.
  • Checks oversized attachments before sending and stops with instructions for manual handling.
  • Uses FloodWait handling and records failures and formatting fallbacks.
  • Can archive reaction metadata from a compatible companion JSON export as text notes. It cannot restore the original native reaction bar, original voters, or historical reaction counts as live Telegram reactions.
  • Uses a Telegram bot session for sending. It does not require a personal-account login for the migration itself.

Requirements

  • Python 3.10 or newer recommended.
  • A Telegram API ID and API hash from my.telegram.org.
  • A bot token from @BotFather.
  • The bot must be an administrator in the destination channel with permission to post.
  • A Telegram Desktop export with its HTML pages and media folders kept together.

Install

pip install telegram-migration

That's it. You get the telegram-migration command and the telegram_migration Python package.

For video-sticker metadata support (optional, only needed if your archive contains .webm stickers and ffprobe isn't already installed):

pip install "telegram-migration[media]"

From source (for development)

If you want to modify the tool or run the test suite:

git clone https://github.com/ifaac/Telegram_Migration.git
cd Telegram_Migration
python -m venv .venv
# activate the venv, then:
python -m pip install -e ".[media]"
python -m unittest discover -s tests -v

Configure

  1. Copy .env.example to .env.
  2. Replace the example API hash, bot token, and destination peer with your own values.
  3. Put your exported Telegram HTML pages and media folders in the directory specified by TELEGRAM_EXPORT_DIR.

Example .env:

TELEGRAM_API_ID=12345678
TELEGRAM_API_HASH=replace_with_your_api_hash
TELEGRAM_BOT_TOKEN=replace_with_your_new_bot_token
TELEGRAM_CHANNEL_PEER=-1001234567890
TELEGRAM_EXPORT_DIR=./telegram_export
TELEGRAM_DRY_RUN=true
TELEGRAM_SKIP_CONFIRMATION=false

TELEGRAM_CHANNEL_PEER may be a numeric peer ID (for example -1001234567890) or a public @username. The program intentionally has no preconfigured destination ID.

TELEGRAM_API_ID has no default: use your own from my.telegram.org. The program refuses to start while any credential is missing or still an example value.

Optional settings (defaults in brackets):

Variable Meaning
TELEGRAM_MAX_UPLOAD_MB [2000] Stop at any message with a file larger than this many MiB (0 disables the check). 2000 MiB is believed to be Telegram's MTProto ceiling; verify it with your bot.
TELEGRAM_BATCH_LIMIT [1000] Stop after this many messages (sent + failed).
TELEGRAM_SEND_DELAY [2.5] Seconds to wait between messages.
TELEGRAM_SESSION_NAME [colab_bot_restorer_session] Name of the Telethon session file.

Google Colab

Colab Secrets named TELEGRAM_API_ID, TELEGRAM_API_HASH, TELEGRAM_BOT_TOKEN and TELEGRAM_CHANNEL_PEER are read automatically (enable notebook access for each). Run the migration inside the notebook so it can read the secrets (a !python ... command starts a separate process that cannot):

!pip install -q telegram-migration
from telegram_migration import Settings, run

settings = Settings.from_env(
    base_dir="/content/drive/MyDrive/ChatExport_2025-01-01",   # your export folder
    dry_run=True,                                                # preview first
)
run(settings)

When the preview looks right, run the cell again with dry_run=False.

The program mounts Google Drive automatically when the export directory is under /content/drive/ and Drive is not mounted yet.

Run safely

Dry-run is enabled by default. Start by running:

python -m telegram_migration

(telegram-migration does the same once the package is installed.) Review the reported source file count, destination channel title/ID, and dry-run summary. No messages are sent in a dry run.

When you are satisfied with the preview, either set TELEGRAM_DRY_RUN=false in .env or pass --send:

python -m telegram_migration --send

The program asks you to confirm the target before sending; keep TELEGRAM_SKIP_CONFIRMATION=false (or omit --yes) so it does.

Command-line options override the environment (python -m telegram_migration --help lists them):

Option Effect
--export-dir PATH Folder with messages.html and the media folders.
--channel PEER Destination: numeric peer ID or @username.
--dry-run / --send Preview only / really send.
--yes Skip the confirmation prompt.
--max-upload-mb N Stop at files larger than N MiB (0 disables).
--batch-limit N, --delay SECONDS Stop after N messages / wait between messages.
--env-file PATH Load this .env file instead of ./.env.

Use as a Python library

from telegram_migration import Settings, run

settings = Settings.from_env()      # TELEGRAM_* variables, .env file, Colab Secrets
run(settings)                       # or: run(dry_run=False)

Settings is a plain dataclass, so everything can also be given directly (Settings(api_id=..., base_dir=..., max_upload_mb=500)). run() works from scripts and notebooks; await telegram_migration.run_async(settings) is the coroutine version. Async runs scope settings to their task, so concurrent migrations do not overwrite each other's configuration. run_async() does not apply the nest_asyncio patch used by run() inside an already-running event loop. Importing the package reads no environment variables and performs no file I/O; credentials problems raise telegram_migration.ConfigError.

Telegram's message limit is 4096 UTF-16 units for every account, and media captions are limited to 1024 for a bot (2048 applies only to Premium user accounts, and a bot is not one). The defaults are text_limit=4096 and caption_limit=1024; you can lower them to split text into shorter parts. Settings.validate() rejects text_limit or caption_limit below 4, and text_limit above 4096 and caption_limit above 1024 (the actual ceiling for a bot).

Progress files and resuming

The script keeps its progress files inside the export directory. These include the ID map, partial-send state, pending replies, failure log, manual-upload list, formatting audit, and reply audit. Keep these files between runs if you want resumability. Do not commit them to a public repository: they can reveal private chat metadata.

If the program stops at a file larger than the configured upload ceiling, follow the printed manual-upload instructions before running the program again. The script can mark that source message to be skipped on the next run.

Project layout

The code is a small package instead of one long script:

Module Responsibility
config The Settings dataclass, environment/.env/Colab-Secrets loading, validation.
pipeline The run itself: stream the export, send each message, resume, stop cleanly.
htmlexport, richtext, htmlutil Read a message out of the HTML export; turn its rich text into Telegram entities.
exportmedia, mediatools Find and classify media; optional ffmpeg/hachoir helpers.
sending, uploads, formatfallback Send text/albums/voice/stickers/forwards; upload files; retry without formatting if Telegram rejects it.
progress, replylinks, reaction_archive ID map and resume state; reply verification; reaction notes.
bigfiles Oversize-file check, manual-upload list and stop instructions.
textutil, constants UTF-16 text/entity helpers; fixed Telegram limits.
cli, runner Command line and the synchronous run().

Known limitations

  • Telegram service messages (for example, join/leave events and title changes) are intentionally excluded.
  • The destination messages are not identical to the original Telegram records. Sender names and timestamps are represented as text in reconstructed messages unless a native forward is possible.
  • The source HTML and media folder must contain the required data. A missing attachment cannot be recreated by this program.
  • If Telegram rejects formatting entities, the script can fall back to plain text for that message; check migration_formatting_dropped.jsonl.
  • Reaction archives are informational text notes, not native Telegram reactions.
  • Native forwards may be unavailable for source messages whose origins are inaccessible; the program's configured forwarding fallback may use a textual header.
  • Files above the upload limit (max_upload_mb) cause a clean stop and require manual handling. Files below the configured limit can still fail due to network, server, permissions, or media constraints.

Development checks

The repository includes an offline regression suite. The tests do not sign in to Telegram, contact Telegram servers, send messages, or require API credentials.

Run the tests from the repository root:

python -m pip install -r requirements.txt
python -m compileall -q telegram_migration
python -m unittest discover -s tests -v

The suite covers rich-text parsing and UTF-16 entity offsets, long-text chunking, export parsing, dates and replies, media classification, GIF/sticker upload options, upload-size checks, progress and ID-map persistence, manual-upload records, reaction metadata, settings and the command line, the module structure (no undefined names), and complete runs against a fake Telegram client: dry run, resume, deferred replies, formatting fallback, and the oversize stop. The fake clients cannot post to a real Telegram chat.

Security

  • Never commit .env, bot tokens, API hashes, Telegram session files, exports, media, or migration state.
  • If you have ever exposed a bot token, revoke it with BotFather and replace it before running the program.
  • Treat API hashes and Telethon session files as private credentials.
  • Avoid sharing migration logs publicly; they may contain source message IDs and message-specific error details.

License

GNU General Public License v3.0

Metadata

Release files for telegram-migration 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for telegram-migration 0.1.1
File Size Uploaded Provenance Actions
telegram_migration-0.1.1.tar.gz 72.9 kB With provenance Details for telegram_migration-0.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for telegram-migration 0.1.1
File Interpreter ABI Platform Actions
telegram_migration-0.1.1-py3-none-any.whl Python 3 none any Details for telegram_migration-0.1.1-py3-none-any.whl

Total release size: 135.7 kB

Release files / telegram_migration-0.1.1.tar.gz

Download URL telegram_migration-0.1.1.tar.gz
Size 72.9 kB
Tags Source
SHA-256 checksum
How to use checksums
6b8b13e4e5a8093771c72ff801ac108a862ca438f490a3a30735a051bda308cd
BLAKE2b-256 checksum
How to use checksums
ac1deae23a8b613d3554d3a32a3828e42e41659edba1ffe609f8a0aada90f9df
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

Release files / telegram_migration-0.1.1-py3-none-any.whl

Download URL telegram_migration-0.1.1-py3-none-any.whl
Size 62.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
92baf4f2dc5463823eadd3b699789a2c5c6ba94ee33314656d7cb3e08f9b5f63
BLAKE2b-256 checksum
How to use checksums
66ae6043f5859e062c71248862ff6f3d9d4b7effb72a0601ea399f17d3d73aca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

Security for telegram-migration 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for telegram_migration-0.1.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page