Skip to content

Fix broken reply-history mapping; harden web dashboard - #1

Merged
klept0 merged 2 commits into
mainfrom
fix/history-mapping-and-hardening
Jul 1, 2026
Merged

klept0 merged 2 commits into
mainfrom
fix/history-mapping-and-hardening

Conversation

@klept0

@klept0 klept0 commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Summary

Full review found and fixed the following, highest priority first:

  • Reply/history mapping was broken on every queued forward. MessageForwardService.forward_message/forward_album returned the source message as a placeholder when queuing a send (the real send happens later, asynchronously). Forward._forward_message/_forward_album then used that placeholder to record history, storing (source_chat, source_id) -> (source_chat, source_id) instead of the real destination mapping — silently breaking reply-threading, since Telegram always constructs a real queue. Fixed via an on_sent callback that fires with the actual sent message once the queue worker sends it; added regression tests for the queued path (tests/test_forwarding_service.py).
  • requirements.txt was missing deps web/app.py needs (fastapi, uvicorn, jinja2, pydantic, python-dotenv, loguru, aiofiles) and pinned an obsolete typing backport that can break stdlib typing on 3.10+.
  • Web dashboard /api/* routes had no authentication and bound to 0.0.0.0 by default — added a required X-API-Key check (fails closed if API_KEY unset) and switched the default bind to 127.0.0.1.
  • Web dashboard's startup handler checked env vars the app never populates and did nothing (pass) — telegram_client was never initialized. It now reuses the session created by the CLI (python main.py) via Credentials.get_all() + Telegram.create().
  • Removed source/utils/RateLimiter.py (RateLimitedQueue) — zero references anywhere.
  • Removed the postgres/redis/prometheus docker-compose.yml services — unused by any app code, and two of them referenced ./docker/postgres/init.sql / ./docker/prometheus.yml, which don't exist in the repo (their profiles would fail to start).
  • Removed dead env vars (TELEGRAM_API_ID, TELEGRAM_API_HASH, TELEGRAM_PHONE, BOT_TOKEN, LOG_LEVEL, LOG_FILE, MAX_CONCURRENT_TASKS, MESSAGE_QUEUE_DELAY, SECRET_KEY) from .env.example/docker-compose.yml — none were ever read by the app.
  • Credentials.py now reuses Constants.CREDENTIALS_FILE_PATH instead of a separately hardcoded path, and writes the file 0600 (contains a plaintext api_hash).
  • Cleaned up a no-op try/except in Bot.start() and silent exception-swallowing in main.py's shutdown().
  • Updated README.md/CHANGELOG.md to match.

Test plan

  • pytest tests/ — 16/16 pass (includes 2 new regression tests for the history-mapping fix)
  • ruff check — clean
  • black --check on touched files (reformatted the 2 files with new code)
  • Verified web/app.py imports and registers all routes with the new deps
  • Verified the X-API-Key auth dependency: 503 with no API_KEY set, 401 on missing/wrong key, 200 with the correct key

klept0 and others added 2 commits June 30, 2026 21:39
The highest-priority bug: MessageForwardService returned the source
message as a placeholder for queued sends, and Forward used that
placeholder to record history mappings — so every queued forward
stored (source_chat, source_id) -> (source_chat, source_id) instead
of the real destination, silently breaking reply-threading on every
run. Fixed via an on_sent callback that fires with the real sent
message once the queue worker actually sends it, with regression
tests covering the queued path.

Also:
- requirements.txt was missing several deps web/app.py needs (fastapi,
  uvicorn, jinja2, pydantic, python-dotenv, loguru, aiofiles) and
  pinned an obsolete `typing` backport package.
- web/app.py's /api/* routes had no authentication and defaulted to
  binding 0.0.0.0; added a required X-API-Key check (fails closed if
  API_KEY isn't set) and switched the default bind to 127.0.0.1.
- web/app.py's startup handler checked env vars the app never
  populates and did nothing; it now actually connects using the
  session created by the CLI.
- Removed source/utils/RateLimiter.py (unused, zero references) and
  the postgres/redis/prometheus docker-compose services (unused by
  any app code; two referenced config files that don't exist in the
  repo).
- Removed unused env vars from .env.example/docker-compose.yml that
  were never read anywhere in the codebase.
- Credentials.py now reuses Constants.CREDENTIALS_FILE_PATH instead
  of a separately hardcoded path, and writes the file 0600 since it
  holds a plaintext api_hash.
- Cleaned up a no-op try/except in Bot.start() and silent exception
  swallowing in main.py's shutdown().

Co-Authored-By: Claude <noreply@anthropic.com>
CI was entirely broken (this is the repo's first PR, so it seems
nobody had actually run it):

- pyproject.toml had no `dev`/`docs` extras, so `pip install -e ".[dev]"`
  silently installed none of ruff/mypy/pytest-asyncio/pytest-cov and
  every test-matrix job failed at "ruff: command not found". Added
  both extras with the tools each CI step needs.
- `security` and `docs` jobs failed before running any step because
  actions/upload-artifact@v3 is hard-blocked as deprecated. Bumped to
  v4, plus setup-python v4->v5, cache v3->v4, codecov-action v3->v4.
- `docs` job's `make html` had no docs/ directory to build against;
  added a minimal Sphinx scaffold (conf.py, index.rst, Makefile).
- `mypy source/` had 16 pre-existing type errors: implicit-Optional
  defaults and a return-type mismatch in Chat.py, a tzinfo|None
  attribute access in DateUtils.py, and false positives from
  InquirerPy's execute_async() being mistyped as returning None.
  Fixed the real issues and added [tool.mypy] config (ignore_missing_imports,
  skip-follow-imports for InquirerPy) so the check reflects actual bugs.
- `ruff format --check` failed on 13 files that had only ever been
  run through black (via pre-commit), which disagrees with ruff format
  on some edge cases. Reformatted the tree so CI and pre-commit agree.

Co-Authored-By: Claude <noreply@anthropic.com>
@klept0
klept0 merged commit 3b1b5a1 into main Jul 1, 2026
7 checks passed
@klept0
klept0 deleted the fix/history-mapping-and-hardening branch July 1, 2026 02:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant