import json from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from greenapi import ( extract_text_message, get_device_key_by_instance_id, send_text_message, send_url_button, ) from engine.conversation_engine import process_message from messages import log_message from handoff.sales import create_sales_handoff from handoff.support import create_support_handoff app = FastAPI() app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=False, allow_methods=["POST", "OPTIONS"], allow_headers=["*"], ) @app.get("/health") async def health(): print(">>> /health called") return {"ok": True, "service": "up"} @app.get("/") async def root(): return { "ok": True, "service": "ADK Bot API", "message": "Server is running" } @app.get("/receive-test") async def receive_test(): print(">>> /receive-test called") return {"ok": True, "message": "receive endpoint reachable"} @app.post("/receive") async def webhook_receiver(req: Request): print("========== /receive HIT ==========") try: raw_body = await req.body() print(">>> RAW BODY BYTES:") print(raw_body) except Exception as e: print(">>> FAILED TO READ RAW BODY:", str(e)) raw_body = b"" try: headers = dict(req.headers) print(">>> HEADERS:") print(json.dumps(headers, ensure_ascii=False, indent=2)) except Exception as e: print(">>> FAILED TO READ HEADERS:", str(e)) body = None try: if raw_body: body = json.loads(raw_body.decode("utf-8")) else: body = {} except Exception as e: print(">>> FAILED TO PARSE JSON:", str(e)) return JSONResponse( status_code=200, content={ "ok": True, "debug": "request reached /receive but body is not valid JSON" } ) print("===== RECEIVED JSON =====") print(json.dumps(body, ensure_ascii=False, indent=2)) print("=========================") # مؤقتًا نطبع typeWebhook حتى لو مختلفة webhook_type = body.get("typeWebhook") print(">>> typeWebhook:", webhook_type) if webhook_type != "incomingMessageReceived": print(">>> ignored non-incoming webhook") return { "ok": True, "ignored": True, "typeWebhook": webhook_type } instance_data = body.get("instanceData") or {} sender_data = body.get("senderData") or {} msg_data = body.get("messageData") or {} # مبدئيًا هنطبع كل الاحتمالات print(">>> instanceData:", instance_data) print(">>> senderData:", sender_data) print(">>> messageData:", msg_data) # مهم: بعض payloads فيها idInstance وبعضها wid فقط instance_id = str( instance_data.get("idInstance") or instance_data.get("wid") or "" ) chat_id = sender_data.get("chatId") text_message = extract_text_message(msg_data) print(">>> resolved instance_id:", instance_id) print(">>> resolved chat_id:", chat_id) print(">>> resolved text_message:", text_message) if not instance_id or not chat_id or not text_message: print(">>> skipped because missing required fields") return { "ok": True, "skipped": True, "resolved_instance_id": instance_id, "resolved_chat_id": chat_id, "resolved_text_message": text_message, } device_key = get_device_key_by_instance_id(instance_id) print(">>> resolved device_key:", device_key) if not device_key: print(">>> Unknown instance_id:", instance_id) return { "ok": True, "skipped": True, "reason": "unknown_instance_id", "instance_id": instance_id } customer_phone = chat_id.replace("@c.us", "") bot_number = device_key try: log_message(customer_phone, bot_number, "in", text_message) except Exception as e: print(">>> failed to log incoming message:", str(e)) try: result = process_message( bot_number=bot_number, customer_phone=customer_phone, text=text_message ) print(">>> engine result:") print(json.dumps(result, ensure_ascii=False, indent=2)) except Exception as e: print(">>> engine error:", str(e)) fallback_reply = "تمام، حصلت مشكلة بسيطة. ممكن تعيد إرسال رسالتك مرة تانية؟" try: status, txt = send_text_message(device_key, chat_id, fallback_reply) print(">>> fallback send status:", status) print(">>> fallback send body:", txt) log_message(customer_phone, bot_number, "out", fallback_reply) except Exception as send_err: print(">>> failed to send fallback:", str(send_err)) return JSONResponse( status_code=200, content={"ok": True, "error": str(e), "fallback_sent": True} ) reply = (result.get("reply") or "").strip() action = result.get("action") if action and action.get("type") == "url_button": try: status, txt = send_url_button( device_key=device_key, chat_id=chat_id, header=action.get("header", "مرحباً بك"), body=action.get("body", reply), footer=action.get("footer", ""), url=action.get("url"), button_text=action.get("button_text", "فتح الرابط") ) print(">>> sent url button status:", status) print(">>> sent url button body:", txt) try: log_message(customer_phone, bot_number, "out", action.get("body", reply)) except Exception as e: print(">>> failed to log outgoing url button body:", str(e)) except Exception as e: print(">>> failed to send url button:", str(e)) fallback_reply = action.get("body", reply) or "تمام، افتح الرابط من فضلك." try: status, txt = send_text_message(device_key, chat_id, fallback_reply) print(">>> url fallback send status:", status) print(">>> url fallback send body:", txt) log_message(customer_phone, bot_number, "out", fallback_reply) except Exception as send_err: print(">>> failed to send url fallback:", str(send_err)) else: try: status, txt = send_text_message(device_key, chat_id, reply) print(">>> sent text status:", status) print(">>> sent text body:", txt) try: log_message(customer_phone, bot_number, "out", reply) except Exception as e: print(">>> failed to log outgoing text:", str(e)) except Exception as e: print(">>> failed to send text:", str(e)) if action and action.get("type") == "handoff": department = action.get("department") summary = action.get("summary", "") metadata = result.get("flow_data", {}) try: if department == "sales": create_sales_handoff( customer_phone=customer_phone, bot_number=bot_number, summary=summary, metadata=metadata ) elif department == "support": create_support_handoff( customer_phone=customer_phone, bot_number=bot_number, summary=summary, metadata=metadata ) print(">>> handoff created:", department) except Exception as e: print(">>> failed to create handoff:", str(e)) return { "ok": True, "device_key": device_key, "customer_phone": customer_phone, "state": result.get("next_state"), "has_action": bool(action), } @app.post("/complaints/callback") async def complaints_callback(req: Request): print("========== /complaints/callback HIT ==========") try: payload = await req.json() except Exception: return JSONResponse(status_code=400, content={"ok": False, "error": "invalid json"}) print("===== COMPLAINT CALLBACK =====") print(json.dumps(payload, ensure_ascii=False, indent=2)) print("==============================") uid = payload.get("uid", "") complaint_number = payload.get("complaint_number", "") status = payload.get("status", "") return { "ok": True, "uid": uid, "complaint_number": complaint_number, "status": status }