Render Bot correct image
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
4394fa1b7b
commit
f656f04a5f
@ -15,7 +15,7 @@ def _build_keyboard(buttons: Optional[List[dict]]) -> Optional[InlineKeyboardMar
|
|||||||
|
|
||||||
Supported kinds:
|
Supported kinds:
|
||||||
- open_url: {"label":"...", "kind":"open_url", "url":"https://..."}
|
- open_url: {"label":"...", "kind":"open_url", "url":"https://..."}
|
||||||
- callback_api:{"label":"...", "kind":"callback_api", "action":"rerun",
|
- callback_api: {"label":"...", "kind":"callback_api", "action":"rerun",
|
||||||
"params": {...}, "state_token":"..."}
|
"params": {...}, "state_token":"..."}
|
||||||
"""
|
"""
|
||||||
if not buttons:
|
if not buttons:
|
||||||
@ -45,7 +45,7 @@ def _build_keyboard(buttons: Optional[List[dict]]) -> Optional[InlineKeyboardMar
|
|||||||
payload = json.dumps(data, separators=(",", ":"), ensure_ascii=False)
|
payload = json.dumps(data, separators=(",", ":"), ensure_ascii=False)
|
||||||
except Exception:
|
except Exception:
|
||||||
payload = '{"e":"bad"}'
|
payload = '{"e":"bad"}'
|
||||||
# Telegram doc: 1–64 bytes recommended
|
# Telegram recommends <=64 bytes for callback_data
|
||||||
if len(payload.encode("utf-8")) > 64:
|
if len(payload.encode("utf-8")) > 64:
|
||||||
logger.warning("renderer: callback_data too long (%sB); trimming",
|
logger.warning("renderer: callback_data too long (%sB); trimming",
|
||||||
len(payload.encode("utf-8")))
|
len(payload.encode("utf-8")))
|
||||||
@ -103,9 +103,34 @@ async def render_spec(*, bot: Bot, chat_id: int, spec: Dict) -> List[Message]:
|
|||||||
if not (file_id or media_url):
|
if not (file_id or media_url):
|
||||||
logger.warning("renderer: photo without file_id/media_url; skipping")
|
logger.warning("renderer: photo without file_id/media_url; skipping")
|
||||||
continue
|
continue
|
||||||
msg = await bot.send_photo(chat_id=chat_id, photo=file_id or media_url,
|
try:
|
||||||
caption=caption, reply_markup=kb)
|
msg = await bot.send_photo(chat_id=chat_id,
|
||||||
|
photo=file_id or media_url,
|
||||||
|
caption=caption,
|
||||||
|
reply_markup=kb)
|
||||||
sent.append(msg)
|
sent.append(msg)
|
||||||
|
except TelegramError as te:
|
||||||
|
# Typical: "BadRequest: Wrong type of the web page content"
|
||||||
|
logger.exception("renderer.photo_error send_photo err=%s url=%s", te, media_url)
|
||||||
|
# Fallback 1: try as document (Telegram is more permissive)
|
||||||
|
try:
|
||||||
|
msg = await bot.send_document(chat_id=chat_id,
|
||||||
|
document=file_id or media_url,
|
||||||
|
caption=caption,
|
||||||
|
reply_markup=kb)
|
||||||
|
sent.append(msg)
|
||||||
|
except TelegramError as te2:
|
||||||
|
logger.exception("renderer.photo_fallback_doc_error err=%s url=%s", te2, media_url)
|
||||||
|
# Fallback 2: plain text with link
|
||||||
|
fallback_text = (caption + "\n" if caption else "") + (media_url or "")
|
||||||
|
if fallback_text.strip():
|
||||||
|
try:
|
||||||
|
msg = await bot.send_message(chat_id=chat_id,
|
||||||
|
text=fallback_text,
|
||||||
|
reply_markup=kb)
|
||||||
|
sent.append(msg)
|
||||||
|
except TelegramError as te3:
|
||||||
|
logger.exception("renderer.photo_fallback_text_error err=%s url=%s", te3, media_url)
|
||||||
|
|
||||||
elif mtype == "document":
|
elif mtype == "document":
|
||||||
file_id = m.get("file_id")
|
file_id = m.get("file_id")
|
||||||
|
@ -8,6 +8,7 @@ import openai
|
|||||||
from telegram import Update, Bot
|
from telegram import Update, Bot
|
||||||
from django.http import JsonResponse, HttpResponse
|
from django.http import JsonResponse, HttpResponse
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
from django.core.cache import cache
|
||||||
from asgiref.sync import sync_to_async
|
from asgiref.sync import sync_to_async
|
||||||
|
|
||||||
from .models import TelegramBot
|
from .models import TelegramBot
|
||||||
@ -18,15 +19,11 @@ from .handlers import (
|
|||||||
next_route, complete_stop, missed_stop, city_eco_score,
|
next_route, complete_stop, missed_stop, city_eco_score,
|
||||||
available_jobs, accept_job, next_pickup, complete_pickup, private_eco_score
|
available_jobs, accept_job, next_pickup, complete_pickup, private_eco_score
|
||||||
)
|
)
|
||||||
|
from .renderer import render_spec
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
openai.api_key = os.getenv("OPENAI_API_KEY")
|
openai.api_key = os.getenv("OPENAI_API_KEY")
|
||||||
|
|
||||||
# at top with other imports
|
|
||||||
from .renderer import render_spec
|
|
||||||
# top imports
|
|
||||||
from django.core.cache import cache
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
# Canonical req.v1 builder
|
# Canonical req.v1 builder
|
||||||
@ -152,6 +149,7 @@ def build_req_v1(update: Dict[str, Any], bot_name: str) -> Dict[str, Any]:
|
|||||||
}
|
}
|
||||||
return env
|
return env
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
# Existing helper flows
|
# Existing helper flows
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
@ -231,6 +229,7 @@ async def transcribe_with_whisper(update: Update, bot: Bot) -> Optional[str]:
|
|||||||
)
|
)
|
||||||
return transcript_str.strip() if transcript_str else None
|
return transcript_str.strip() if transcript_str else None
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
# Webhook
|
# Webhook
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
@ -262,25 +261,27 @@ async def telegram_webhook(request, bot_name: str):
|
|||||||
|
|
||||||
# ----- Idempotency / retry guard (drops duplicates for ~90s) -----
|
# ----- Idempotency / retry guard (drops duplicates for ~90s) -----
|
||||||
upd_id = payload.get("update_id")
|
upd_id = payload.get("update_id")
|
||||||
# Fallback if no update_id: use message_id + user_id
|
cbq = payload.get("callback_query") or {}
|
||||||
fallback_msg = (payload.get("message") or {}).get("message_id")
|
cbq_id = cbq.get("id")
|
||||||
fallback_user = ((payload.get("message") or {}).get("from") or {}).get("id")
|
msg = payload.get("message") or {}
|
||||||
|
fallback_msg_id = msg.get("message_id")
|
||||||
|
fallback_user = (msg.get("from") or {}).get("id")
|
||||||
|
|
||||||
dedupe_key = None
|
dedupe_key = None
|
||||||
if upd_id is not None:
|
if upd_id is not None:
|
||||||
dedupe_key = f"tg:update:{upd_id}"
|
dedupe_key = f"tg:update:{upd_id}"
|
||||||
elif fallback_msg and fallback_user:
|
elif cbq_id:
|
||||||
dedupe_key = f"tg:msg:{fallback_msg}:{fallback_user}"
|
dedupe_key = f"tg:cbq:{cbq_id}"
|
||||||
|
elif fallback_msg_id and fallback_user:
|
||||||
|
dedupe_key = f"tg:msg:{fallback_msg_id}:{fallback_user}"
|
||||||
|
|
||||||
if dedupe_key:
|
if dedupe_key:
|
||||||
# cache.add returns True if the key did not exist (first time), False otherwise
|
|
||||||
if not cache.add(dedupe_key, "1", timeout=90):
|
if not cache.add(dedupe_key, "1", timeout=90):
|
||||||
logger.info("tg.idempotent.skip key=%s", dedupe_key)
|
logger.info("tg.idempotent.skip key=%s", dedupe_key)
|
||||||
return JsonResponse({"status": "duplicate_skipped"})
|
return JsonResponse({"status": "duplicate_skipped"})
|
||||||
# -----------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
|
|
||||||
|
# Build canonical req.v1 (log only for now)
|
||||||
# Build canonical req.v1 (LOG ONLY for now)
|
|
||||||
try:
|
try:
|
||||||
canon = build_req_v1(payload, bot_name)
|
canon = build_req_v1(payload, bot_name)
|
||||||
logger.info("tg.canonical env=%s", json.dumps(canon, ensure_ascii=False))
|
logger.info("tg.canonical env=%s", json.dumps(canon, ensure_ascii=False))
|
||||||
@ -290,8 +291,7 @@ async def telegram_webhook(request, bot_name: str):
|
|||||||
# Convert to telegram.Update
|
# Convert to telegram.Update
|
||||||
update = Update.de_json(payload, Bot(token=bot_instance.token))
|
update = Update.de_json(payload, Bot(token=bot_instance.token))
|
||||||
|
|
||||||
# --- TEMP: demo renderer (safe to delete later) ----------------------
|
# --- TEMP demo: send a text + photo + buttons ----------------------
|
||||||
# If user sends "/_render_demo", send a text + photo + buttons
|
|
||||||
if update.message and (update.message.text or "").strip() == "/_render_demo":
|
if update.message and (update.message.text or "").strip() == "/_render_demo":
|
||||||
bot = Bot(token=bot_instance.token)
|
bot = Bot(token=bot_instance.token)
|
||||||
spec = {
|
spec = {
|
||||||
@ -300,7 +300,8 @@ async def telegram_webhook(request, bot_name: str):
|
|||||||
{"type": "text", "text": "Demo: render_spec text ✅"},
|
{"type": "text", "text": "Demo: render_spec text ✅"},
|
||||||
{
|
{
|
||||||
"type": "photo",
|
"type": "photo",
|
||||||
"media_url": "https://upload.wikimedia.org/wikipedia/commons/5/5f/Alameda_Central_CDMX.jpg",
|
# Use a known-good image URL (or host your own under /static/)
|
||||||
|
"media_url": "https://picsum.photos/seed/polisplexity/800/480.jpg",
|
||||||
"caption": "Demo: render_spec photo ✅"
|
"caption": "Demo: render_spec photo ✅"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -312,8 +313,7 @@ async def telegram_webhook(request, bot_name: str):
|
|||||||
}
|
}
|
||||||
await render_spec(bot=bot, chat_id=update.effective_chat.id, spec=spec)
|
await render_spec(bot=bot, chat_id=update.effective_chat.id, spec=spec)
|
||||||
return JsonResponse({"status": "ok", "render_demo": True})
|
return JsonResponse({"status": "ok", "render_demo": True})
|
||||||
# --------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
if not update.message:
|
if not update.message:
|
||||||
# No message (e.g., callback handled elsewhere in legacy); ack anyway
|
# No message (e.g., callback handled elsewhere in legacy); ack anyway
|
||||||
|
Loading…
x
Reference in New Issue
Block a user