fix(telegram): async webhook handler + deduplicate callback responses
- Webhook returns 200 immediately, processing runs in background task → Telegram no longer retries, eliminates duplicate callbacks and 400 errors - Consolidate answer_callback_query call to top of handler (before heavy work) - Add logger.info/error for callback actions to aid debugging Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1020526e76
commit
e9c472b722
2 changed files with 31 additions and 15 deletions
|
|
@ -677,7 +677,14 @@ def api_n8n_ingest(request: Request) -> dict:
|
||||||
|
|
||||||
@app.post("/telegram/webhook")
|
@app.post("/telegram/webhook")
|
||||||
async def telegram_webhook(request: Request) -> dict:
|
async def telegram_webhook(request: Request) -> dict:
|
||||||
"""Receive updates from Telegram Bot API."""
|
"""Receive updates from Telegram Bot API.
|
||||||
|
|
||||||
|
Returns 200 immediately so Telegram never retries the same update.
|
||||||
|
Actual processing runs in a background task.
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
|
||||||
# Verify secret token
|
# Verify secret token
|
||||||
secret = settings.telegram_webhook_secret
|
secret = settings.telegram_webhook_secret
|
||||||
if secret:
|
if secret:
|
||||||
|
|
@ -691,12 +698,14 @@ async def telegram_webhook(request: Request) -> dict:
|
||||||
except Exception:
|
except Exception:
|
||||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid JSON")
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid JSON")
|
||||||
|
|
||||||
try:
|
async def _process():
|
||||||
handle_update(update)
|
loop = asyncio.get_event_loop()
|
||||||
except Exception as exc:
|
try:
|
||||||
import logging
|
await loop.run_in_executor(None, lambda: handle_update(update))
|
||||||
logging.getLogger(__name__).error("Telegram update handler error: %s", exc)
|
except Exception as exc:
|
||||||
|
logging.getLogger(__name__).error("Telegram update handler error: %s", exc)
|
||||||
|
|
||||||
|
asyncio.create_task(_process())
|
||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -400,9 +400,19 @@ def _handle_callback(callback_query: dict[str, Any]) -> None:
|
||||||
answer_callback_query(query_id, "Artikel nicht gefunden")
|
answer_callback_query(query_id, "Artikel nicht gefunden")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Answer Telegram immediately so the spinning indicator stops
|
||||||
|
action_labels = {
|
||||||
|
"rewrite": "✏️ Artikel wird neu geschrieben …",
|
||||||
|
"discard": "❌ Artikel verworfen",
|
||||||
|
"override": "➕ Artikel wird verarbeitet …",
|
||||||
|
"reject": "🚫 Abgelehnt",
|
||||||
|
}
|
||||||
|
answer_callback_query(query_id, action_labels.get(action, ""))
|
||||||
|
edit_message_reply_markup(chat_id, message_id)
|
||||||
|
|
||||||
|
logger.info("Callback: action=%s article_id=%s", action, article_id)
|
||||||
|
|
||||||
if action == "rewrite":
|
if action == "rewrite":
|
||||||
answer_callback_query(query_id, "✏️ Artikel wird neu geschrieben …")
|
|
||||||
edit_message_reply_markup(chat_id, message_id)
|
|
||||||
try:
|
try:
|
||||||
_pipeline.rewrite_and_update_draft(article_id)
|
_pipeline.rewrite_and_update_draft(article_id)
|
||||||
updated = get_article_by_id(article_id)
|
updated = get_article_by_id(article_id)
|
||||||
|
|
@ -411,28 +421,25 @@ def _handle_callback(callback_query: dict[str, Any]) -> None:
|
||||||
slot = suggest_publish_slot()
|
slot = suggest_publish_slot()
|
||||||
notify_new_draft(updated, score=_get_relevance_score(updated), suggested_publish_at=slot)
|
notify_new_draft(updated, score=_get_relevance_score(updated), suggested_publish_at=slot)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
logger.error("Rewrite #%d fehlgeschlagen: %s", article_id, exc)
|
||||||
notify_error(f"Rewrite #{article_id} fehlgeschlagen: {exc}")
|
notify_error(f"Rewrite #{article_id} fehlgeschlagen: {exc}")
|
||||||
|
|
||||||
elif action == "discard":
|
elif action == "discard":
|
||||||
answer_callback_query(query_id, "❌ Artikel verworfen")
|
|
||||||
edit_message_reply_markup(chat_id, message_id)
|
|
||||||
try:
|
try:
|
||||||
_pipeline.discard_article(article_id)
|
_pipeline.discard_article(article_id)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
logger.error("Discard #%d fehlgeschlagen: %s", article_id, exc)
|
||||||
notify_error(f"Verwerfen #{article_id} fehlgeschlagen: {exc}")
|
notify_error(f"Verwerfen #{article_id} fehlgeschlagen: {exc}")
|
||||||
|
|
||||||
elif action == "override":
|
elif action == "override":
|
||||||
answer_callback_query(query_id, "➕ Artikel wird verarbeitet …")
|
|
||||||
edit_message_reply_markup(chat_id, message_id)
|
|
||||||
try:
|
try:
|
||||||
_pipeline.override_rejected_article(article_id)
|
_pipeline.override_rejected_article(article_id)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
logger.error("Override #%d fehlgeschlagen: %s", article_id, exc)
|
||||||
notify_error(f"Override #{article_id} fehlgeschlagen: {exc}")
|
notify_error(f"Override #{article_id} fehlgeschlagen: {exc}")
|
||||||
|
|
||||||
elif action == "reject":
|
elif action == "reject":
|
||||||
answer_callback_query(query_id, "🚫 Abgelehnt")
|
|
||||||
edit_message_reply_markup(chat_id, message_id)
|
|
||||||
update_article_status(article_id, "error", actor="telegram", note="Manuell abgelehnt via Telegram")
|
update_article_status(article_id, "error", actor="telegram", note="Manuell abgelehnt via Telegram")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
answer_callback_query(query_id, "Unbekannte Aktion")
|
logger.warning("Unbekannte Callback-Aktion: %s", action)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue