49 Commits

Author SHA1 Message Date
vvzvlad 3c9ce72b51 fix(media): self-heal zombie media-DC connection so images keep loading
Docker Image CI / build (push) Waiting to run
Media downloads jammed process-wide: Kurigram serializes them through a single
get_file slot (max_concurrent_transmissions=1), and a zombie media-DC connection
(upload.GetFile timing out) held that slot forever. The main-DC watchdog (get_me)
never noticed, and the 60s cache sweep kept re-queuing the failing file, so all
feed images turned into broken placeholders.

- telegram_client: count consecutive download timeouts; after N (default 5) reuse
  _restart_client() to rebuild the media connection — the only recovery signal the
  main-DC watchdog cannot provide. Any success resets the streak.
- telegram_client/config: set max_concurrent_transmissions (default 3) so one hung
  download is no longer an instant total outage (blast-radius limiter only).
- api_server: negative-cache with exponential backoff for repeatedly-failing files
  (skip in background sweep, fast 503+Retry-After in get_media, record/clear in the
  dedup runner and background worker; FloodWait excluded).
- dockercompose/tests: document TG_MAX_CONCURRENT_TRANSMISSIONS and
  MEDIA_TIMEOUT_RESTART_THRESHOLD; update mock config; add self-heal/backoff tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 04:22:31 +03:00
agent_coder 7d6ee0271d feat(stability): stage 6 — lightweight /ping healthcheck that never touches Telegram (#6)
The container healthcheck hit /rss/...?limit=1 (5s timeout): on a cold cache RSS
generation exceeds 5s, or a hung TG RPC makes it hang, so docker/autoheal restarts
the container mid-download and corrupts temp files. Replace it with /ping, which
reflects process/loop liveness (answers instantly, always) plus TG liveness read
from the watchdog's last-probe data — issuing ZERO Telegram RPC.

- telegram_client: public watchdog_last_ok_age() — seconds since the last successful
  watchdog probe (None if never). Pure read of the Stage-1 _wd_last_ok_monotonic
  field; no RPC.
- api_server: /ping route (no token, no TG RPC, no SQLite, no fs scan). healthy =
  connected and (age is None or age < threshold). age is None right after boot =>
  healthy (don't kill before the first probe). connected coerced to bool so the JSON
  "connected" field is always a bool (pre-start reports false, never null).
- config: TG_PING_UNHEALTHY_AFTER knob, default interval*(failures+1)+timeout = 250s
  (how long until the watchdog itself gives up), env-overridable.
- dockercompose.yml: healthcheck -> curl -sf http://127.0.0.1:80/ping, interval 5m,
  timeout 5s, retries 3, start_period 30s. Old /rss check removed, not left behind.
- tests: 10 (healthy/stale/disconnected/fresh-boot/pre-start-null/no-token +
  the anti-regression zero-TG-RPC spy across all branches + the accessor).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 10:34:06 +03:00
claude code agent 444bd3e42f fix(stability): stage 2 — atomic media downloads, in-flight dedup, FloodWait->429, bounded semaphore
Fixes flaky static media serving: partial files served as 'ready', truncated
stubs living for an hour, and app-wide hangs under a saturated download path.

2.1 _download_atomic(file_id, final_path, timeout): downloads to a unique
    {final}.part.{hex}, validates size>0, and publishes solely via os.rename
    (atomic on POSIX); a finally ALWAYS removes the partial (timeout/cancel/
    zero-size/race-loser). Every downloader call routes through it, so a file at a
    FINAL name ({fid} / temp_{fid}) is GUARANTEED complete (grep-proven: the only
    safe_download_media caller passes a .part. path). Big-video timeout scales with
    size (min/max/min-speed knobs, documented in dockercompose.yml). The sweeper
    regex now matches both .part. and legacy .tmp. stubs.
2.2 In-flight dedup registry: the first request for a key runs the download in a
    DETACHED task sharing a Future; the task's finally sets the Future AND pops the
    key. Waiters await asyncio.shield(fut) so a client disconnect / waiter timeout
    cancels only the waiter, never the download — no hung waiters, no stuck key, a
    failed download frees the key for retry.
2.3 FloodWait->429: handler before except RPCError (FloodWait subclasses it),
    Retry-After = min(value + rand(1,30), 300); propagates from the detached task
    through the Future.
2.4 Serving a temp_* file touches its mtime so the 1h sweeper can't delete a
    video out from under a viewer.
2.5 The HTTP download semaphore acquire is bounded (wait_for 30 -> 503 +
    Retry-After); the permit is released only if the acquire succeeded. The
    request-scoped-permit trade-off (a disconnect can transiently exceed the
    download count) is documented inline for stage-7 prod observation.

Tests (tests/test_stage2_static.py, 13): atomic publish/clean on every exit incl.
FloodWait-through-the-finally; concurrent big-video serves no partial; dedup runs
one download, a cancelled waiter doesn't hang others, a failed download frees the
key; FloodWait -> 429; mtime touch; sweeper cleans .part./.tmp./stale temp_ but
keeps fresh files. 193 passed (180 baseline + 13).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 07:40:47 +03:00
vvzvlad 021f16cf14 feat(client): add watchdog heartbeat and diagnostics counters
Introduce `tg_watchdog_heartbeat_every` to emit periodic INFO heartbeats.
Add cumulative diagnostics counters and richer logging for watchdog probes,
restart reasons, and disconnect‑flap triggers. Update defaults and tests
accordingly.
2026-06-04 19:19:22 +03:00
vvzvlad 79b127d406 feat(client): add watchdog and disconnect flap handling
Introduce an active watchdog that probes the Telegram client to detect
zombie sessions and restart them in‑process. Add configurable disconnect
flap detection with a sliding window to trigger restarts after repeated
disconnects. New environment variables and config entries are added, and
the Kurigram dependency is now version‑pinned.
2026-06-04 19:02:09 +03:00
vvzvlad ab8f15d49d feat(api): add atomic temp file handling for media downloads
- Use unique temporary file paths with UUID to avoid race conditions during concurrent downloads.
- Perform atomic rename of the temp file to the final cache location and handle existing concurrent downloads.
- Clean up zero‑size and stale temporary files, including new “.tmp.<hex>” pattern.
- Extend cleanup logic to detect and remove race‑condition temp files.
- Validate and convert environment variables (TG_API_ID, TG_PROXY_PORT, API_PORT) with clear error messages and proper exit handling.
- Flush prints and replace `os._exit` with `sys.exit` for graceful termination.
- Fix URL regex in `PostParser` and guard against missing channel usernames when generating media URLs.
- Deep‑copy message lists in RSS generator to prevent mutation across calls.
- Propagate `FloodWait` exceptions to be handled by the API server instead of retrying internally.
- Enhance `TelegramClient` disconnect handling with a brief pause, shutdown check, and reconnection reset.
- Refine auth retry logic to check for actual `KeyError` instances.
2026-05-17 23:49:18 +03:00
vvzvlad 09692edf06 feat(config): add mtproto proxy support
Introduce optional MTProto proxy configuration sourced from environment variables, expose related settings in `config.py`, document the variables in `dockercompose.yml`, and pass the proxy configuration to the Telegram client initialization.
2026-04-05 18:09:23 +03:00
vvzvlad 02c29f8692 fix(telegram): add shutdown guard to avoid duplicate restarts
Add a `_shutting_down` flag to `TelegramClient` to suppress disconnect handling during intentional shutdown. The flag is set before sending SIGTERM in `_restart_app` and checked in `_on_disconnect` to ignore those events. Minor formatting adjustments were also applied to `api_server.py` (try block and `uvicorn.run` arguments).
2026-03-16 04:13:39 +03:00
vvzvlad 14f8db0a32 refactor(client): add optional session param to _on_disconnect 2026-03-16 04:05:39 +03:00
vvzvlad 3c2b4ce544 perf(downloads): implement concurrent download queue with semaphore limiting and retry logic
Add queue-based background download system to improve performance and reliability:
- Introduce DOWNLOAD_SEMAPHORE to limit concurrent downloads to 3
- Add asyncio.Queue (maxsize 100) with dedicated worker for background processing
- Implement safe_get_messages and safe_download_media wrappers with timeout protection (30s and 120s)
- Add retry logic for KeyError auth failures with 5s backoff
- Replace synchronous sequential downloads with asynchronous queued processing
- Prevent event loop blocking by queuing files instead of immediate download
2026-01-01 14:14:26 +03:00
vvzvlad 43516d6dab refactor api_server and TelegramClient: remove force_exit function and replace with sys.exit for process termination, update restart logic to use SIGTERM instead of SIGKILL for graceful shutdown 2025-06-10 15:03:09 +03:00
vvzvlad 521c3dc394 implement forceful termination in api_server and TelegramClient: add force_exit function for immediate process shutdown, update SIGTERM handler for forceful exit, and adjust restart logic in connection_handler to use SIGKILL 2025-06-10 14:28:51 +03:00
vvzvlad d6c3725b02 implement graceful shutdown in api_server: add SIGTERM handler for proper termination and enhance error handling for server startup issues 2025-06-10 14:06:56 +03:00
vvzvlad 2c9ebf139f refactor connection_handler in TelegramClient: increase wait time to 5 seconds before restart, implement process termination and forking for more reliable restarts 2025-06-10 03:58:33 +03:00
vvzvlad 223ae57929 add delay in connection_handler to ensure resources are freed before process restart 2025-06-10 03:38:33 +03:00
vvzvlad c94090c4b5 update _on_disconnect method in TelegramClient: change to async for better handling of disconnections 2025-06-10 03:25:54 +03:00
vvzvlad be28a11db6 implement connection handling in TelegramClient: add disconnect tracking and application restart on multiple disconnections 2025-04-27 18:31:29 +04:00
vvzvlad cb419c97af update pylint and pylance disable comments 2025-04-18 18:00:02 +03:00
vvzvlad 422e2adc2d Add shebang and encoding declaration to multiple Python files 2025-04-03 01:48:34 +03:00
vvzvlad 583a4c4b3c Add uvloop support for asyncio in api_server and telegram_client; update requirements.txt 2025-03-21 14:29:57 +03:00
vvzvlad 60b253c1c1 Refactor session handling and configuration management 2025-02-10 05:09:29 +03:00
vvzvlad b0e03ce6e8 Enhance media retrieval with file unique ID and improved error handling 2025-02-03 02:28:58 +03:00
vvzvlad 7d5f151793 Update media URL generation to include chat username and message ID 2025-02-03 02:06:51 +03:00
vvzvlad 1693e5c0b0 Add media file ID tracking and update file ID retrieval method 2025-02-02 23:35:07 +03:00
vvzvlad f360f1167c BIG refactoring 2025-02-02 18:51:50 +03:00
vvzvlad 2a6470462d Add channel title and icon to RSS feed generation 2025-02-02 03:56:42 +03:00
vvzvlad 533fc8ba31 Fix media URL handling by stripping leading slashes 2025-02-02 03:50:12 +03:00
vvzvlad ca0286a18b Skip pinned service messages in Telegram client parsing 2025-02-02 03:41:52 +03:00
vvzvlad 6b1e75946d Add support for Voice, Animation, and WebPage media types in Telegram client 2025-02-02 03:22:06 +03:00
vvzvlad 3ad9a6461b Enhance media parsing with comprehensive media type support and robust error handling 2025-02-02 02:58:36 +03:00
vvzvlad 1c190b69ac Refactor author information extraction in Telegram client 2025-02-02 02:22:39 +03:00
vvzvlad 2842cebcca Modify message parsing to include raw text, views, and media group ID 2025-02-02 02:15:18 +03:00
vvzvlad 9bb0d8a021 Add RSS feed generation endpoint for Telegram channels 2025-02-02 01:59:14 +03:00
vvzvlad d1ae60a14c Add configurable Pyrogram bridge URL and log level settings 2025-02-02 01:00:41 +03:00
vvzvlad 3f198ffa33 Enhance media parsing with web page photo and poll support 2025-02-02 00:49:47 +03:00
vvzvlad 63a3c4a988 Add media download endpoint and refactor media handling 2025-02-02 00:34:54 +03:00
vvzvlad 5bcac3f494 Disable complex text entity processing in Telegram client 2025-02-01 23:39:46 +03:00
vvzvlad 75a2cca8bd Improve media group processing and message retrieval in Telegram client 2025-02-01 21:11:14 +03:00
vvzvlad 96d316fcb5 Enhance Telegram message parsing with additional metadata extraction 2025-02-01 20:47:21 +03:00
vvzvlad e7f7520f79 Simplify media group processing and message parsing logic 2025-02-01 20:37:20 +03:00
vvzvlad f72b9af544 Add comprehensive raw message parsing and metadata extraction 2025-02-01 20:29:06 +03:00
vvzvlad 19f2c8b3cc Refactor Telegram message parsing and title generation 2025-02-01 20:19:18 +03:00
vvzvlad 6f1cca6646 Include author information in combined Telegram message result 2025-02-01 20:17:16 +03:00
vvzvlad 7a678bf08e Add author information extraction to Telegram message parsing 2025-02-01 20:15:22 +03:00
vvzvlad b33fb2e060 Improve title generation with better truncation logic 2025-02-01 20:12:03 +03:00
vvzvlad 8fe4a1e906 Add title generation for Telegram posts and improve media preview styling 2025-02-01 20:10:19 +03:00
vvzvlad bcac493e4b Enhance message parsing and HTML rendering for Telegram posts 2025-02-01 18:31:14 +03:00
vvzvlad 20221c78e1 add html support 2025-02-01 17:52:23 +03:00
vvzvlad 6e367b3ac9 first work version 2025-02-01 16:47:46 +03:00