diff --git a/.env.example b/.env.example index 09339f9..71795ce 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,25 @@ +# Copy this file to .env and fill in the values on each machine. +# .env is gitignored — secrets and per-machine paths never leave the device. + +# --- Trello (shared across machines) --- TRELLO_API_KEY=your_trello_api_key_here TRELLO_TOKEN=your_trello_token_here + +# --- Telegram API credentials (shared across machines: one app registration) --- TELEGRAM_API_ID= TELEGRAM_API_HASH= + +# --- Per-machine: absolute paths to the telegram-mcp install on THIS machine --- +# TELEGRAM_MCP_DIR — root of the telegram-mcp checkout (used by the session generator) +# TELEGRAM_MCP_BIN — the telegram-mcp executable inside that checkout's .venv +TELEGRAM_MCP_DIR=/absolute/path/to/telegram +TELEGRAM_MCP_BIN=/absolute/path/to/telegram/.venv/bin/telegram-mcp + +# --- Per-machine Telegram session strings --- +# DO NOT copy these between machines. Each device/IP must have its own session, +# or Telegram will permanently revoke the auth key. +# Generate on THIS machine with: +# pnpm tg:session:usulsu -> writes TELEGRAM_SESSION_STRING +# pnpm tg:session:helper -> writes TELEGRAM_SESSION_STRING_HELPER TELEGRAM_SESSION_STRING= +TELEGRAM_SESSION_STRING_HELPER= diff --git a/.mcp.json b/.mcp.json index 07a2bca..6e9a232 100644 --- a/.mcp.json +++ b/.mcp.json @@ -18,17 +18,11 @@ }, "telegram-usulpro": { "command": "bash", - "args": [ - "/home/usul/workspace/projects/cv-2026/scripts/telegram-mcp.sh", - "usulsu" - ] + "args": ["scripts/telegram-mcp.sh", "usulsu"] }, "telegram-helper": { "command": "bash", - "args": [ - "/home/usul/workspace/projects/cv-2026/scripts/telegram-mcp.sh", - "helper" - ] + "args": ["scripts/telegram-mcp.sh", "helper"] } } } diff --git a/CLAUDE.md b/CLAUDE.md index 7c3baab..a86bc2a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -136,6 +136,57 @@ After a run, triage `telegram_inbox.json` and promote promising postings to [`tr --- +## Bootstrap on a new machine + +This project runs on multiple machines (VPS, laptops) with different filesystem paths. The committed config is fully portable; everything machine-specific lives in `.env` and `.secrets/` (both gitignored). When Oleg sets up a new device, or when something Telegram-related breaks ("MCP not connecting", "session revoked", "TELEGRAM_MCP_BIN not set", etc.), walk through this. + +### One-time setup on a fresh checkout + +1. **`.env`** — copy from the template, then fill in: + ```bash + cp .env.example .env + ``` + Required keys: + - `TRELLO_API_KEY`, `TRELLO_TOKEN` — same across machines (copy from another device's `.env`). + - `TELEGRAM_API_ID`, `TELEGRAM_API_HASH` — same across machines (one app registration). + - `TELEGRAM_MCP_DIR` — **per-machine** absolute path to the `telegram-mcp` checkout on THIS machine. + - `TELEGRAM_MCP_BIN` — **per-machine** absolute path to the `telegram-mcp` executable, typically `$TELEGRAM_MCP_DIR/.venv/bin/telegram-mcp`. + - `TELEGRAM_SESSION_STRING`, `TELEGRAM_SESSION_STRING_HELPER` — leave empty; generated in step 3. + +2. **Node deps**: + ```bash + pnpm install + ``` + +3. **Generate Telegram sessions on THIS machine** (one per account): + ```bash + pnpm tg:session:usulsu # QR-login as @usulpro (main) + pnpm tg:session:helper # QR-login as @samuishka (helper) + ``` + Each command opens a QR code in the terminal. Scan it from the corresponding Telegram account on Oleg's phone. The script writes the session string into `.env` and a copy into `.secrets/session_.txt`. + + **Critical rule — sessions are per-device:** never copy `TELEGRAM_SESSION_STRING*` between machines. A Telegram auth key used from two IPs gets permanently revoked, and **both** devices lose access until re-authorized. Always run the regen script on each new machine. + +4. **Reconnect MCP servers** in Claude Code (`/mcp` → reconnect both `telegram-usulpro` and `telegram-helper`), or restart Claude Code. + +### How paths stay portable + +- `.mcp.json` uses a relative path (`scripts/telegram-mcp.sh`) — Claude Code launches project-scoped MCP servers with the project root as CWD, so this resolves correctly on any machine. +- `scripts/telegram-mcp.sh` and `scripts/regen_telegram_session.sh` read `TELEGRAM_MCP_BIN` / `TELEGRAM_MCP_DIR` from `.env`. They fail with an explicit error message if the variable is missing or points at a non-existent path — never guess a default. + +### Troubleshooting cheat sheet + +| Symptom | Cause | Fix | +|---|---|---| +| MCP server `telegram-usulpro` / `telegram-helper` won't start | `.env` missing, or `TELEGRAM_MCP_BIN` not set / not executable on this machine | Check the server's stderr in Claude Code logs; the script prints exactly what's missing. Fill in `.env`. | +| `session string for 'usulsu' is empty in .env` | Sessions weren't generated on this machine yet | `pnpm tg:session:usulsu` (and/or `:helper`) | +| `generator not found at .../.venv/bin/python` | `TELEGRAM_MCP_DIR` points at the wrong place, or `.venv` not created in the telegram-mcp checkout | Fix the path in `.env`; in the telegram-mcp checkout run its venv setup | +| Telegram suddenly logs us out / revokes the key | A session string got reused across two machines/IPs | Regen on each affected machine separately with `pnpm tg:session:`; never copy session strings | +| `scripts/telegram-mcp.sh: No such file or directory` from Claude Code | `.mcp.json` got hardcoded back to an absolute path | Restore relative form: `"args": ["scripts/telegram-mcp.sh", "usulsu"]` | +| `list_telegram_channels.py` / `fetch_telegram_jobs.py` fail to auth | `TELEGRAM_SESSION_STRING` in `.env` doesn't match the usulsu account (these direct Telethon scripts always use usulsu) | Regen with `pnpm tg:session:usulsu` | + +--- + ## Folder layout ``` diff --git a/scripts/regen_telegram_session.sh b/scripts/regen_telegram_session.sh index 910bbec..97d9e8d 100755 --- a/scripts/regen_telegram_session.sh +++ b/scripts/regen_telegram_session.sh @@ -27,20 +27,27 @@ case "$ACCOUNT" in esac PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)" -TELEGRAM_DIR="${TELEGRAM_MCP_DIR:-/home/usul/workspace/projects/my-utils/telegram}" ENV_FILE="$PROJECT_ROOT/.env" SECRETS_DIR="$PROJECT_ROOT/.secrets" OUT_FILE="$SECRETS_DIR/session_${ACCOUNT}.txt" -[ -f "$ENV_FILE" ] || { echo "missing $ENV_FILE" >&2; exit 1; } +[ -f "$ENV_FILE" ] || { echo "missing $ENV_FILE (copy .env.example and fill it in)" >&2; exit 1; } set -a; . "$ENV_FILE"; set +a if [ -z "${TELEGRAM_API_ID:-}" ] || [ -z "${TELEGRAM_API_HASH:-}" ]; then echo "TELEGRAM_API_ID / TELEGRAM_API_HASH missing in $ENV_FILE" >&2 exit 1 fi + +TELEGRAM_DIR="${TELEGRAM_MCP_DIR:-}" +if [ -z "$TELEGRAM_DIR" ]; then + echo "TELEGRAM_MCP_DIR not set in $ENV_FILE" >&2 + echo "point it at the telegram-mcp checkout on THIS machine, e.g.:" >&2 + echo " TELEGRAM_MCP_DIR=/path/to/telegram" >&2 + exit 1 +fi [ -x "$TELEGRAM_DIR/.venv/bin/python" ] || { echo "generator not found at $TELEGRAM_DIR/.venv/bin/python" >&2 - echo "set TELEGRAM_MCP_DIR to your telegram-mcp checkout" >&2; exit 1; } + echo "check TELEGRAM_MCP_DIR in $ENV_FILE and that its .venv is created" >&2; exit 1; } mkdir -p "$SECRETS_DIR" tmp="$(mktemp)"; trap 'rm -f "$tmp"' EXIT diff --git a/scripts/telegram-mcp.sh b/scripts/telegram-mcp.sh index a53155e..3866c62 100755 --- a/scripts/telegram-mcp.sh +++ b/scripts/telegram-mcp.sh @@ -1,20 +1,20 @@ #!/usr/bin/env bash # Launch the telegram-mcp server for one account, reading API creds and the # session string from the project .env. Used by .mcp.json so that NO secrets -# are hardcoded in the committed config. +# and NO machine-specific paths are hardcoded in the committed config. # # Usage (from .mcp.json): # bash scripts/telegram-mcp.sh usulsu # main account -> TELEGRAM_SESSION_STRING # bash scripts/telegram-mcp.sh helper # samuishechka -> TELEGRAM_SESSION_STRING_HELPER # -# The telegram-mcp binary path can be overridden with TELEGRAM_MCP_BIN. +# Per-machine config (must live in .env, not committed): +# TELEGRAM_MCP_BIN absolute path to the telegram-mcp executable on THIS machine set -euo pipefail ACCOUNT="${1:?usage: telegram-mcp.sh }" PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)" -BIN="${TELEGRAM_MCP_BIN:-/home/usul/workspace/projects/my-utils/telegram/.venv/bin/telegram-mcp}" -[ -f "$PROJECT_ROOT/.env" ] || { echo "missing $PROJECT_ROOT/.env" >&2; exit 1; } +[ -f "$PROJECT_ROOT/.env" ] || { echo "missing $PROJECT_ROOT/.env (copy .env.example and fill it in)" >&2; exit 1; } set -a; . "$PROJECT_ROOT/.env"; set +a case "$ACCOUNT" in @@ -28,9 +28,19 @@ if [ -z "${TELEGRAM_API_ID:-}" ] || [ -z "${TELEGRAM_API_HASH:-}" ]; then exit 1 fi if [ -z "${TELEGRAM_SESSION_STRING:-}" ]; then - echo "session string for '$ACCOUNT' is empty in .env — regenerate it with:" >&2 + echo "session string for '$ACCOUNT' is empty in .env — regenerate it on THIS machine with:" >&2 echo " pnpm tg:session:$ACCOUNT" >&2 + echo "(do not copy session strings between machines — Telegram revokes the key)" >&2 exit 1 fi +BIN="${TELEGRAM_MCP_BIN:-}" +if [ -z "$BIN" ]; then + echo "TELEGRAM_MCP_BIN not set in $PROJECT_ROOT/.env" >&2 + echo "point it at the telegram-mcp executable on THIS machine, e.g.:" >&2 + echo " TELEGRAM_MCP_BIN=/path/to/telegram/.venv/bin/telegram-mcp" >&2 + exit 1 +fi +[ -x "$BIN" ] || { echo "TELEGRAM_MCP_BIN=$BIN is not executable on THIS machine" >&2; exit 1; } + exec "$BIN"