KOL feeds: fix dead/blocked sources, drop stale feeds (29→25)

Feed-health pass over KOL_FEEDS:
- raoulpal: stale Substack (last 2024-05) → Real Vision podcast feed
- dampedspring: paywalled (0 entries) → free "Damped Spring 101" Substack
- unchained: Cloudflare 403 → canonical Megaphone podcast feed
- lynalden: Cloudflare 202 → FeedBurner mirror
- glassnode: recovered via httpx http2=True (was 403 on HTTP/1.1)
- browser User-Agent + Accept headers on feed fetch
- removed dead feeds with no active replacement: placeholder,
  dragonfly, niccarter, eugene
- pin h2==4.3.0 (required by http2=True)

All 25 remaining feeds verified fetching real body content; newest
post per feed ≤88d. Bundles in-flight KOL-module work already in the
working tree (kol_x ingest, migration 027, tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
k
2026-06-09 22:55:16 +08:00
parent 213bb911e3
commit 54884f3e24
38 changed files with 2340 additions and 322 deletions
+25
View File
@@ -998,6 +998,31 @@ async def run_bot_loop() -> None:
return
logger.info("Telegram bot loop starting (long-poll mode).")
# ── Startup drain ────────────────────────────────────────────────────
# On restart, Telegram may replay up to 24h of unacknowledged updates.
# The most dangerous replays are stale /adopt callback_query items that
# would re-fire an adoption the user already dealt with. Fix: pull all
# pending updates with timeout=0 (non-blocking), advance offset past
# them without processing, then start the real loop fresh.
try:
drain_url = TG_API.format(token=token, method="getUpdates")
async with httpx.AsyncClient(timeout=10) as client:
r = await client.get(drain_url, params={"timeout": 0, "limit": 100})
if r.status_code == 200:
pending = r.json().get("result", [])
if pending:
drain_offset = pending[-1]["update_id"] + 1
# ACK by sending offset back — Telegram won't re-deliver these.
async with httpx.AsyncClient(timeout=10) as client:
await client.get(drain_url, params={"timeout": 0, "offset": drain_offset})
logger.info(
"Startup drain: skipped %d stale update(s), offset now %d",
len(pending), drain_offset,
)
except Exception as exc:
logger.warning("Startup drain failed (non-fatal): %s", exc)
offset: Optional[int] = None
backoff = 1.0