Webhook (Flask) and polling (momo-telegram-bot) consumed the same
Telegram update_id, causing /menu callbacks to fire twice. Add a
shared dedup module backed by telegram_update_dedup table (300s TTL,
60s cleanup) with in-memory fallback, wired into both paths.
Polling launcher now skips startup when webhook is configured to
prevent dual-consumption at the source.
38 tests across webhook, menu keyboards, telegram_api, dedup guard,
and trend bot service.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- normalize URLs at write time (scheduler crawlers, routes) to drop
javascript:/EC404/placeholder i_code (momo_/manual_/pchome_)
- add global click+auxclick guard in base.html and ewoooc_base.html
that intercepts blocked MOMO URLs and redirects to safe i_code URL
- per-page dashboards reuse the same isLikelyMomoIcode validation
- /api/track_momo_link records blocked events for diagnosis
- ship sanitize_momo_urls.py to clean existing polluted DB rows
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>