telegram-logger is a service built on Telethon that automatically stores incoming/outgoing messages in SQLite, buffers media, and upon deletion/editing sends restored content to a separate log chat. The project is designed to run as a systemd service or as a Docker container.
Huge thanks to kawaiiDango and their project on which this service is based: https://github.com/kawaiiDango/telegram-delete-logger
Use at your own risk. You are solely responsible for compliance with Telegram rules/API Terms, applicable laws, and any consequences of use (including account limits or bans).
Main scenarios:
-
Logs messages (text + serialized media metadata) to SQLite.
-
Buffers media files in
media/for recovery of:- deleted messages,
- optionally restricted messages (
noforwards, self-destruct).
-
Tracks message deletions:
- for text — sends restored text to the log chat;
- for media — attempts to retrieve the file from the buffer, optionally re-fetches the message if needed, and sends the file to the log chat.
-
Optionally saves text edit history (format
before/after). -
Optionally encrypts deleted media in
media_deleted/(AES-256-GCM). -
Periodically cleans up data:
- old DB records by TTL (separately per chat type),
- outdated buffer files by TTL.
-
Optionally manual saving of restricted messages via link:
- send one or multiple links (space-separated) to the log chat;
- supported link formats:
https://t.me/...,https://t.me/c/...,tg://openmessage...,tg://privatepost....
-
Exposes an HTTP health endpoint (default
/health) for monitoring.
In Telegram, a channel and its discussion group (comments chat) are two separate chats with different IDs. A common case: the channel itself is useful, but comments (and bots inside them) generate a lot of noise/spam.
If you want to disable logging of spam, add the ID of the discussion chat, not the channel ID, to IGNORED_IDS.
The easiest way is to look at a message link (if Telegram shows the t.me/c/... format).
Example:
https://t.me/c/1234567890/1234
1234567890— chat/channel ID (internal identifier),1234— message number.
If a username is used instead, e.g. https://t.me/some_channel/1234, that is a username, not an ID. To convert username → numeric ID, you can use the bot @username_to_id_bot.
If certain channels/chats are not needed or create noise, add their IDs to IGNORED_IDS. Messages from them will not be logged, and your database and media buffer will not be cluttered.
The service uses the Telegram API (MTProto) via Telethon — for this you need API_ID and API_HASH.
-
Open:
https://my.telegram.org -
Log in using your phone number (Telegram will send a code).
-
Go to API development tools.
-
Create an application (Create new application):
- App title: any (e.g.
telegram-logger) - Short name: any (e.g.
tglogger) - Other fields can be filled arbitrarily.
- App title: any (e.g.
-
After creation you will see:
- App api_id →
API_ID - App api_hash →
API_HASH
- App api_id →
Add them to .env:
API_ID=123456
API_HASH="your_api_hash_here"user.session is the Telethon authorization file. It is created on the first successful login (Telegram code / 2FA password if enabled). After that, the service can run without re-authentication as long as the session remains valid.
On first container run, it will prompt for the confirmation code and create the session file in the mounted /data.
docker run --rm -it \
-v $(pwd)/data:/data \
-e API_ID=123456 \
-e API_HASH="your_api_hash_here" \
-e LOG_CHAT_ID=-1001234567890 \
ghcr.io/pve-kaston/telegram-message-logger:latestAfter successful login, the file will appear on the host:
./data/db/user.session
Important:
- run with
-itso you can enter the code/2FA password; - обязательно mount
-v $(pwd)/data:/data, otherwise the session will remain inside the container and disappear after it is removed.
You can generate user.session in advance using the script from scripts/:
cd scripts
pip install telethon
export API_ID=123456
export API_HASH="your_api_hash_here"
export SESSION_FILE="../src/telegram_logger/data/db/user.session" # optional
python generate_session.pyIf SESSION_FILE is not set, the session will be created in the current directory.
If you already have a ready user.session file, simply place it in:
- Docker:
/data/db/user.session(on host usually./data/db/user.sessionwhen using-v $(pwd)/data:/data)
After that, on subsequent runs, re-authentication (code and 2FA) will not be required.
All parameters are set via .env (or actual environment variables).
API_ID=123456
API_HASH="your_telegram_api_hash_here"
LOG_CHAT_ID=-1001234567890IGNORED_IDS=[-1002222222222222222222, -10033333333333333333333]
LISTEN_OUTGOING_MESSAGES=true
# DATA_ROOT controls where sessions/db/media are stored.
# Usually you DON'T need to set it.
# Docker default: /data
# Non-docker default: <working_directory>/src/data
# DATA_ROOT=/custom/path
BUFFER_ALL_MEDIA=true
BUFFER_NOFORWARDS_CONTENT=false
PROCESS_SELF_DESTRUCT_MEDIA=false
MAX_BUFFER_FILE_SIZE=104857600 # 100 MB
MEDIA_BUFFER_TTL_HOURS=24
ENCRYPT_DELETED_MEDIA=false
DELETED_MEDIA_KEY_B64="base64_32_bytes_key"
MAX_DELETED_MESSAGES_PER_EVENT=100
SAVE_EDITED_MESSAGES=true
DELETE_SENT_GIFS_FROM_SAVED=true
DELETE_SENT_STICKERS_FROM_SAVED=true
SAVE_DELETED_FROM_PRIVATE_CHATS=true
SAVE_DELETED_FROM_GROUPS=true
SAVE_DELETED_FROM_CHANNELS=true
PERSIST_TIME_IN_DAYS_BOT=7
PERSIST_TIME_IN_DAYS_USER=7
PERSIST_TIME_IN_DAYS_CHANNEL=7
PERSIST_TIME_IN_DAYS_GROUP=7
HEALTH_PATH=/health
HEALTH_PORT=8080
HEALTH_ERROR_WINDOW_SECS=120
HEALTH_HOUSEKEEPING_STALE_SECS=600
DEBUG_MODE=falsepython - <<'PY'
import base64, os
print(base64.b64encode(os.urandom(32)).decode())
PY- Python 3.10+
- Telegram
API_ID - Telegram
API_HASH - Target
LOG_CHAT_ID
git clone https://github.com/pve-kaston/telegram-message-logger.git
cd telegram-message-logger
python3 -m venv .venv
source .venv/bin/activate
pip install -r src/requirements.txtCreate .env in the project root:
API_ID=123456
API_HASH=your_api_hash_here
LOG_CHAT_ID=-1001234567890On first start, Telethon will create
data/db/user.session.
python -m telegram_loggerImage:
ghcr.io/pve-kaston/telegram-message-logger:latest
docker run -it \
-v $(pwd)/data:/data \
--env-file .env \
ghcr.io/pve-kaston/telegram-message-logger:latestRun:
docker compose up -dLogs:
docker compose logs -fsudo apt update
sudo apt install -y python3 python3-venv python3-pipsudo mkdir -p /opt/telegram_logger
sudo cp -r . /opt/telegram_logger
cd /opt/telegram_logger
sudo python3 -m venv venv
sudo ./venv/bin/pip install --upgrade pip
sudo ./venv/bin/pip install -r src/requirements.txtsudo mkdir -p /etc/telegram_logger
sudo nano /etc/telegram_logger/.envsudo useradd --system \
--home /opt/telegram_logger \
--shell /usr/sbin/nologin \
telegram_logger
sudo chown -R telegram_logger:telegram_logger /opt/telegram_logger
sudo chown -R telegram_logger:telegram_logger /etc/telegram_loggerEnable:
sudo systemctl daemon-reload
sudo systemctl enable --now telegram-loggerCheck logs:
sudo journalctl -u telegram-logger -fIf ENCRYPT_DELETED_MEDIA=true, deleted media is stored encrypted.
Use the script from scripts/ to decrypt.
Example:
export TELEGRAM_DELETED_MEDIA_KEY_B64="YOUR_BASE64_KEY_HERE"
python3 scripts/decrypt_deleted_media.py \
--enc ./data/media_deleted \
--out ~/telegram-logger-decrypted
TELEGRAM_DELETED_MEDIA_KEY_B64must match theDELETED_MEDIA_KEY_B64used during encryption.