Skip to content

Releases: GeiserX/Telegram-Archive

v7.7.0

29 Apr 13:58
91ccfd3

Choose a tag to compare

Security

  • Viewer now fails closed when credentials are missing — If VIEWER_USERNAME/VIEWER_PASSWORD are not configured, the HTTP API and WebSocket endpoint reject access unless ALLOW_ANONYMOUS_VIEWER=true is explicitly set.
  • Restricted media access is enforced consistently — Media, thumbnails, avatars, and non-chat folders now share centralized chat ACL checks, preventing restricted users from reading _shared files or unrelated chat media.
  • No-download users can no longer fetch original or thumbnail bytes — Accounts and share tokens with no_download=true receive metadata only; direct original media and generated thumbnail URLs return 403, while UI avatars remain available.
  • Internal push events require a secret off-loopback/internal/push requires INTERNAL_PUSH_SECRET for non-loopback/private-network callers, reducing spoofing risk between co-located containers.
  • WebSocket upgrades validate origin — Cross-origin WebSocket connections must be same-origin or explicitly allowed by CORS_ORIGINS.
  • Non-interactive auth hash files are owner-only — Persisted phone_code_hash sidecar files are now created with 0600 permissions.

Fixed

  • Scheduled backups no longer overlap — The scheduler uses a backup lock so initial and cron-triggered jobs cannot run concurrently.
  • FloodWait handling is explicit and bounded — One-shot Telegram API calls now retry through shared helpers and abort instead of sleeping when Telegram asks for waits above MAX_FLOOD_WAIT_SECONDS.
  • FloodWait env parsing is resilient — Invalid MAX_FLOOD_RETRIES and MAX_FLOOD_WAIT_SECONDS values fall back to safe defaults instead of crashing imports.
  • Media downloads finalize atomically — Temporary .part files are moved into place only when an actual file exists, preserving Telethon-selected extensions and avoiding bogus stored paths.
  • Telegram contact, geo, and poll media are metadata-only — These message types no longer trigger file download attempts.
  • Database URL precedence is consistent — Entrypoint migrations and realtime notifier/listener mode detection now honor DATABASE_URL before DB_TYPE, including postgres://, postgresql://, postgresql+asyncpg://, and SQLite URLs.
  • Database migration coverage includes app-state tables — SQLite-to-PostgreSQL migration now includes viewer accounts, sessions, tokens, folders, forum topics, push subscriptions, and settings.
  • Share token URLs avoid query-string leakage — Generated links use #token= fragments and preserve subpath deployments.

Changed

  • Deletion listening is safer by defaultLISTEN_DELETIONS now defaults to false so archives do not mirror Telegram deletions unless explicitly configured.
  • Docker examples pin the 7.7.0 release — Compose and README snippets now reference drumsergio/telegram-archive:7.7.0 and drumsergio/telegram-archive-viewer:7.7.0.
  • Viewer compose binds to localhost by default — The example viewer service binds 127.0.0.1:8000:8000 and documents reverse-proxy/auth requirements before public exposure.
  • CI and release checks are stricter — Docker publish workflows run ruff and pytest before publishing, shellcheck tracks main, Docker Hub description sync covers both images, and release checks match the documented local test command.

Documentation

  • Viewer authentication setup is documented — README and .env.example now show required viewer credentials and the explicit anonymous opt-in.
  • Chat include filters are documented as allow-lists — Examples now correctly show CHAT_TYPES=groups,channels when including one specific channel alongside groups.
  • Operational safety docs were refreshed — README and .env.example now describe deletion mirroring, flood-wait controls, proxy header trust, and internal push secrets.

Tests

  • Added regression coverage for fail-closed viewer auth, no-download media restrictions, thumbnail ACLs, WebSocket subscription filtering, internal push auth, scheduler locking, flood-wait aborts, atomic downloads, DATABASE_URL behavior, non-interactive auth hash reuse, and migration model enumeration.

📋 Full changelog: docs/CHANGELOG.md

v7.6.4

25 Apr 18:00
cae9b3c

Choose a tag to compare

Fixed

  • Improved General topic test suite — Renamed unprofessional test data, removed redundant @pytest.mark.asyncio decorators (project uses asyncio_mode = "auto"), converted setup to a proper pytest fixture, and added edge case tests for nonexistent topics, topic_id=0, and topic+search filter interaction. Contributed by @tondeaf in #122 (follow-up).

📋 Full changelog: docs/CHANGELOG.md

v7.6.3

25 Apr 17:27
e8d6e62

Choose a tag to compare

Fixed

  • Edit notifications no longer silently dropped on long messages — The 500-char truncation guard only protected data["message"]["text"] (new_message path), leaving data["new_text"] (edit path) unprotected. A 4096-char emoji edit could produce a 16KB payload exceeding PostgreSQL's 8KB NOTIFY limit, causing a silent pg_notify error. Both paths are now truncated via a shared _truncate_notify_data() helper. (#123 follow-up)
  • Use pg_notify() with bound parameters for PostgreSQL NOTIFY — Replaces f-string SQL interpolation that was vulnerable to asyncpg $N placeholder parsing and fragile manual single-quote escaping. Contributed by @tondeaf in #123.
  • Push secret comparison is now timing-safe/internal/push endpoint used != for bearer token comparison; switched to secrets.compare_digest() consistent with the rest of the auth layer.
  • Test assertions use stable TextClause.text attribute — Replaced str(stmt) with stmt.text for SQLAlchemy SQL assertions, avoiding reliance on undocumented __str__ behavior.

📋 Full changelog: docs/CHANGELOG.md

v7.6.2

25 Apr 17:10
4f25bd0

Choose a tag to compare

Fixed

  • FloodWaitError no longer crashes get_dialogs() or get_me() — PR #124 set flood_sleep_threshold=0 globally but only wrapped 2 of ~20 API call sites. The unwrapped get_dialogs() and get_me() calls could crash the entire backup or prevent startup. Both are now wrapped with bounded flood-wait retry logic.
  • Negative e.seconds from Telegram no longer causes zero-delay retry storms — Sleep duration is now clamped to max(0, ...) on both the iterator wrapper and the new one-shot retry helper.
  • Invalid FLOOD_WAIT_LOG_THRESHOLD env var no longer crashes mid-backup — Bare int() parsing replaced with defensive try/except that falls back to the default of 10 seconds.
  • iter_messages_with_flood_retry now rejects reverse=False — The resume tracking (max(resume_from, msg.id)) is only correct for ascending iteration. A ValueError is now raised if reverse=True is not passed, preventing silent data corruption from future misuse.
  • Documented FLOOD_WAIT_LOG_THRESHOLD — Added to .env.example alongside the other logging variables.

📋 Full changelog: docs/CHANGELOG.md

v7.6.1

19 Apr 19:44

Choose a tag to compare

Fixed

  • Forwarded media from private channels no longer creates broken placeholders — When a message forwarded from a private channel contains a document with an inaccessible file reference (media.document=None), _get_media_type() now correctly returns None instead of "document". Previously this caused a broken telegram_file_id of "None", a failed download attempt, and a misleading "Will download on next backup" placeholder that would never resolve. Applies to both scheduled backup and real-time listener (#125)

📋 Full changelog: docs/CHANGELOG.md

v7.6.0 — Topic Filtering & Symlink Fix

18 Apr 13:01

Choose a tag to compare

Added

  • Topic filtering for forum supergroups — New SKIP_TOPIC_IDS environment variable to exclude specific topics from backup while keeping the rest of the chat. Format: chat_id:topic_id,.... Works in both scheduled backup and real-time listener flows (#117)

Fixed

  • Dangling dedup symlinks no longer cause infinite redownload loops — When DEDUPLICATE_MEDIA is enabled and VERIFY_MEDIA runs, dangling symlinks (where the target was renamed by Telethon) are now detected via os.path.lexists() instead of os.path.exists(), which follows symlinks. The download return value is now captured to use the actual on-disk filename for symlink targets. Stale symlinks are removed before recreation to prevent Errno 17 (file exists) errors. Applies to both scheduled backup and real-time listener (#115)

📋 Full changelog: docs/CHANGELOG.md

v7.5.0 — SOCKS5 Proxy Support

13 Apr 10:31

Choose a tag to compare

What's New

SOCKS5 Proxy Support (#104)

You can now route all Telegram connections through a SOCKS5 proxy — useful in regions where Telegram is blocked or behind corporate firewalls.

Configuration (all optional):

TELEGRAM_PROXY_TYPE=socks5
TELEGRAM_PROXY_ADDR=127.0.0.1
TELEGRAM_PROXY_PORT=1080
TELEGRAM_PROXY_USERNAME=
TELEGRAM_PROXY_PASSWORD=
TELEGRAM_PROXY_RDNS=false

Proxy support is applied consistently across all code paths: backup, real-time listener, auth setup, and standalone scripts.

Improvements

  • Validation hardening: port range (1–65535), username/password pairing, boolean RDNS parsing, and case-insensitive proxy type
  • Security: proxy endpoint details logged at DEBUG (not INFO) to avoid exposing infrastructure topology
  • Test isolation: proxy integration tests use scoped fixtures instead of global sys.modules mutation
  • Dependency: added python-socks[asyncio]>=2.7.1 (required by Telethon for SOCKS5 transport)

Contributors

Thanks to @samnyan for the proxy feature contribution!

What's Changed

New Contributors

Full Changelog: v7.4.2...v7.5.0

v7.4.2 — Correctness & Hygiene

31 Mar 11:47
dfa165b

Choose a tag to compare

Correctness & Release Hygiene

Fixes

  • Listener shutdown KeyError (Medium): _log_stats() referenced non-existent keys from MassOperationProtector.get_stats(). A clean shutdown would raise KeyError. Fixed to use actual keys (rate_limits_triggered, operations_blocked, chats_rate_limited).
  • Pin/unpin realtime (Low): Full pipeline now works end-to-end: listener emits PIN → notifier delivers → handle_realtime_notification() forwards to WebSocket → browser reloads pinned messages. Previously the relay in main.py was missing, making the frontend handler dead code.
  • pyproject.toml version sync (Low): Was stuck at 7.2.0 since v7.2.0. Now synced with __init__.py at 7.4.2.
  • WebSocket subscribe ACL (Low): Server now sends subscribe_denied (instead of subscribed) when a restricted user attempts to subscribe to a chat outside their allowed list. Frontend logs the denial.

What's Changed

  • fix: correctness and release hygiene (v7.4.2) by @GeiserX in #100

Full Changelog: v7.4.1...v7.4.2

v7.4.1 — Security Hardening Round 2

31 Mar 11:22
1242bf1

Choose a tag to compare

Security Hardening (Round 2)

Fixes

  • Avatar ACL bypass (Medium): Restricted users can no longer access avatars outside their allowed chats. serve_media() and serve_thumbnail() now extract chat_id from avatar filenames and enforce per-chat scoping.
  • Push endpoint spoofing (Medium): /internal/push now supports an optional INTERNAL_PUSH_SECRET env var as a bearer token. Prevents co-tenant containers from spoofing live events to connected browsers.
  • Reaction recovery data loss (Medium): insert_reactions() now retries ALL reactions after a sequence reset, not just the row that triggered the duplicate-key error. Previously, the return after a single retry silently dropped remaining reactions.
  • Push unsubscribe ownership (Low): POST /api/push/unsubscribe is now scoped to the requesting user's username, preventing cross-user endpoint removal.

New Environment Variable

  • INTERNAL_PUSH_SECRET: Optional shared secret for /internal/push endpoint. Set the same value on both backup and viewer containers in multi-tenant Docker environments. If unset, IP-only auth is used (backward compatible).

What's Changed

  • fix: security hardening round 2 (v7.4.1) by @GeiserX in #99

Full Changelog: v7.4.0...v7.4.1

v7.4.0 — Security Hardening

31 Mar 11:06
0897f54

Choose a tag to compare

Security Hardening

Addresses multiple security findings from code review.

Fixes

  • XSS (High): linkifyText() now percent-encodes raw " and ' in URLs before inserting into href attributes. escapeHtml() via textContent/innerHTML does not escape quotes.
  • Stats filter (Medium): Fixed JSON string-key vs int type mismatch that caused per-chat filtering to silently fail. Also removes media_files/total_size_mb for restricted users (no per-chat breakdown available).
  • Deletion path (Medium): Unknown-chat deletions now resolve the chat ID from DB first, apply rate limiting, skip ambiguous message IDs (same ID in multiple chats), and send viewer notifications.
  • Folders (Low): Restricted users no longer see empty folder names/emoticons for folders with 0 accessible chats.
  • Push endpoint (Low): /internal/push accepts loopback + RFC1918/Docker private IPs to support split-container SQLite mode via VIEWER_HOST/VIEWER_PORT.

Breaking Changes

  • delete_message_by_id_any_chat() replaced by resolve_message_chat_id() in the database adapter. The old method deleted from ALL chats with a matching message ID — the new approach resolves to a single chat first and skips ambiguous cases.

What's Changed

  • fix: security hardening — XSS, push spoofing, metadata leaks, deletion bugs by @GeiserX in #98

Full Changelog: v7.3.2...v7.4.0