# pxy_bots/api/views.py import json from django.http import JsonResponse, HttpResponse from django.views.decorators.csrf import csrf_exempt def health(request): return JsonResponse({"ok": True, "service": "pxy_bots", "schema_ready": ["req.v1", "render.v1"]}) @csrf_exempt def echo_render(request): try: data = json.loads(request.body.decode("utf-8") or "{}") except Exception: data = {} text = (((data.get("input") or {}).get("text")) or "Hola 👋") who = (((data.get("user") or {}).get("id")) or "user") cmd = (((data.get("command") or {}).get("name")) or "none") spec = { "schema_version": "render.v1", "messages": [ {"type": "text", "text": f"echo: user={who} cmd={cmd}"}, {"type": "text", "text": f"you said: {text}"}, ], } return JsonResponse(spec) # pxy_bots/api/views.py import json, string from django.http import JsonResponse, HttpResponseBadRequest from django.views.decorators.csrf import csrf_exempt from pxy_bots.models import TelegramBot, BotReplyTemplate def _ctx_from_req(req): inp = (req.get("input") or {}) loc = inp.get("location") or {} args_raw = (inp.get("args_raw") or "") or (inp.get("text") or "") or "" # Strip /cmd if present for ${args} if args_raw.startswith("/"): parts = args_raw.split(" ", 1) args_raw = parts[1] if len(parts) > 1 else "" return { "user_id": ((req.get("user") or {}).get("id")), "chat_id": ((req.get("chat") or {}).get("id")), "cmd": ((req.get("command") or {}).get("name")), "trigger": ((req.get("command") or {}).get("trigger")), "text": (inp.get("text") or ""), "caption": (inp.get("caption") or ""), "args": args_raw, "lat": loc.get("lat"), "lon": loc.get("lon"), } def _choose_template(bot, trigger, cmd): qs = (BotReplyTemplate.objects .filter(bot=bot, enabled=True, trigger=trigger) .order_by("priority", "id")) t = (qs.filter(command_name=(cmd or None)).first() or qs.filter(command_name__isnull=True).first() or qs.filter(command_name="").first()) return t @csrf_exempt def template_reply(request): if request.method != "POST": return HttpResponseBadRequest("POST only") try: req = json.loads(request.body.decode("utf-8") or "{}") except Exception: return HttpResponseBadRequest("invalid json") bot_name = ((req.get("bot") or {}).get("username")) if not bot_name: return HttpResponseBadRequest("missing bot.username") bot = TelegramBot.objects.filter(name=bot_name, is_active=True).first() \ or TelegramBot.objects.filter(username=bot_name, is_active=True).first() if not bot: return HttpResponseBadRequest("bot not found") trigger = ((req.get("command") or {}).get("trigger")) or "message" cmd = ((req.get("command") or {}).get("name") or "") cmd = cmd.strip().lstrip("/").lower() or None tpl = _choose_template(bot, trigger, cmd) if not tpl: # Soft fallback to help you see wiring issues return JsonResponse({ "schema_version": "render.v1", "messages": [{"type": "text", "text": f"(no template) bot={bot_name} trigger={trigger} cmd={cmd or '(default)'}"}] }) ctx = _ctx_from_req(req) text = string.Template(tpl.text_template or "").safe_substitute(**{k:("" if v is None else v) for k,v in ctx.items()}) msg_list = [] if tpl.media_url: # If media present, put text in caption; otherwise plain text message if text.strip(): msg_list.append({"type": "photo", "media_url": tpl.media_url, "caption": text, "parse_mode": tpl.parse_mode.upper() if tpl.parse_mode!="none" else None}) else: msg_list.append({"type": "photo", "media_url": tpl.media_url}) if (not tpl.media_url) and text.strip(): msg = {"type": "text", "text": text} if tpl.parse_mode != BotReplyTemplate.PARSE_NONE: msg["parse_mode"] = tpl.parse_mode.upper() msg_list.append(msg) spec = {"schema_version": "render.v1", "messages": msg_list} btns = tpl.buttons() if btns: spec["buttons"] = btns return JsonResponse(spec)