# app/api/webhook.py from fastapi import APIRouter, Request, HTTPException, status from fastapi.responses import PlainTextResponse, JSONResponse, Response from pydantic import BaseModel import json from config import VERIFY_TOKEN from models.webhook_model import WebhookEvent from services.webhook_service import ( handle_message_type, mark_message_as_read, encrypt_flow_response_data, decrypt_flow_request_data, send_whatsapp_flow # <--- ADICIONAR decrypt_flow_request_data ) router = APIRouter() class SendFlowRequest(BaseModel): to_number: str # O número para onde enviar o Flow (ex: '5582912345678') # ... (Endpoint GET /webhook) ... # --- Endpoint para Receber Requisições POST (Incluindo TODOS os Payloads Criptografados e Webhook Padrão) --- @router.post("/webhook") async def process_whatsapp_webhook(request: Request): try: body = await request.json() except json.JSONDecodeError as e: print(f"⚠️ Requisição POST recebida com corpo não-JSON ou JSON inválido: {e}") return JSONResponse(status_code=status.HTTP_200_OK, content={"status": "corpo inválido, ignorado"}) except Exception as e: print(f"⚠️ Erro inesperado ao ler o corpo da requisição POST: {e}") return JSONResponse(status_code=status.HTTP_200_OK, content={"status": "erro de leitura, ignorado"}) # --- Lógica para Requisições de Dados de Flow Criptografadas (incluindo Health Check criptografado) --- if isinstance(body, dict) and "encrypted_flow_data" in body and "encrypted_aes_key" in body and "initial_vector" in body: print("🔒 Recebido payload de Flow criptografado.") try: # Descriptografa a requisição para obter os dados, a chave AES e o IV originais decrypted_result = decrypt_flow_request_data( body["encrypted_flow_data"], body["encrypted_aes_key"], body["initial_vector"] ) decrypted_data = decrypted_result["decrypted_payload"] aes_key_from_request = decrypted_result["aes_key"] iv_from_request = decrypted_result["initial_vector"] print(f"🔓 Dados do Flow descriptografados: {json.dumps(decrypted_data, indent=2)}") flow_response_data = { "data": { "status": "active" } } # Processa o tipo de requisição descriptografada if decrypted_data.get("action") == "ping": print("💚 Descriptografado: Ping de dados do Flow. Preparando resposta 'active'.") else: print(f"❓ Descriptografado: Outros dados de Flow. Conteúdo: {decrypted_data}") flow_response_data = {"status": "success", "message": "Dados de Flow processados."} # Criptografa a resposta usando a chave AES e o IV DA REQUISIÇÃO ORIGINAL # A função encrypt_flow_response_data AGORA RETORNA A STRING BASE64 DIRETA encrypted_final_response_string = encrypt_flow_response_data( flow_response_data, aes_key_from_request, iv_from_request ) print(f"DEBUG: Enviando resposta final criptografada (Base64): {encrypted_final_response_string[:50]}...") return Response(content=encrypted_final_response_string, media_type="text/plain") except ValueError as ve: print(f"❌ ERRO de chave ou dados para descriptografia/criptografia de Flow: {ve}") return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={"status": f"Erro de chave ou criptografia: {ve}"}) except Exception as e: print(f"❌ ERRO ao processar dados de Flow criptografados: {e}") return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={"status": f"Erro interno ao processar Flow: {e}"}) # --- Lógica para Eventos de Webhook Padrão do WhatsApp (mensagens, status) --- # Este bloco só será acionado se não for um payload de Flow criptografado # --- Bloco para Eventos Padrão do WhatsApp (mensagens, status, etc.) --- # Este é o bloco que interessa para o teste de sessão elif isinstance(body, dict) and "object" in body and "entry" in body: try: event = WebhookEvent(**body) if event.object != 'whatsapp_business_account': print("❌ Evento recebido, mas não é do tipo 'whatsapp_business_account'. Ignorando.") return JSONResponse(status_code=status.HTTP_404_NOT_FOUND, content={"status": "ignorado"}) for entry in event.entry: for change in entry.changes: if change.field == 'messages': if change.value and change.value.messages: for message in change.value.messages: print(f"\n💬 Mensagem recebida de {message.from_} (Tipo: {message.type}, ID: {message.id})") await mark_message_as_read(message.id) # CHAME handle_message_type PASSANDO O OBJETO 'message' COMPLETO await handle_message_type(message) else: print("⚠️ Evento de 'messages' recebido, mas sem mensagens válidas no payload.") elif change.field == 'statuses': if change.value and change.value.statuses: for status_event in change.value.statuses: print(f"ℹ️ Status da mensagem ID {status_event.id}: {status_event.status} (para {status_event.recipient_id})") # Futuramente: handle_status_event(status_event) else: print("⚠️ Evento de 'statuses' recebido, mas sem status válidos no payload.") # REMOVA OU COMENTE TEMPORARIAMENTE o bloco 'elif change.field == 'flows':' se estiver lá # para focar apenas em mensagens/sessão. # elif change.field == 'flows': # ... else: print(f"⚠️ Evento de campo desconhecido recebido: {change.field}. Payload completo: {change.model_dump_json(indent=2) if hasattr(change, 'model_dump_json') else json.dumps(change.dict(), indent=2)}") return JSONResponse(status_code=status.HTTP_200_OK, content={"status": "evento processado com sucesso"}) except Exception as e: print(f"⚠️ Payload recebido não corresponde ao modelo WebhookEvent ou erro interno: {type(e).__name__}: {e}. Payload: {json.dumps(body)}") return JSONResponse(status_code=status.HTTP_200_OK, content={"status": "payload desconhecido, ignorado"}) # --- Fallback para payloads POST não reconhecidos --- else: print(f"❓ Payload POST recebido que não é Health Check, Flow criptografado, nem WebhookEvent padrão. Ignorando. Payload: {json.dumps(body)}") return JSONResponse(status_code=status.HTTP_200_OK, content={"status": "payload não reconhecido, ignorado"}) @router.post("/send_cadastro_flow") async def trigger_cadastro_flow(request_data: SendFlowRequest): """ Dispara o Flow de cadastro do WhatsApp para um número específico. """ target_number = request_data.to_number # <-- ESTA LINHA DEVE ESTAR AQUI E ACESSÍVEL # --- MUITO IMPORTANTE: Substitua pelo FLOW_ID REAL que você publicou --- FLOW_ID_DO_SEU_CADASTRO_PUBLICADO = 1094799999286205 if FLOW_ID_DO_SEU_CADASTRO_PUBLICADO == "COLOQUE_AQUI_O_FLOW_ID_DO_SEU_CADASTRO_PUBLICADO": raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Por favor, substitua 'COLOQUE_AQUI_O_FLOW_ID_DO_SEU_CADASTRO_PUBLICADO' pelo Flow ID real no código." ) print(f"DEBUG: Disparando Flow de Cadastro (ID: {FLOW_ID_DO_SEU_CADASTRO_PUBLICADO}) para o número: {target_number}") try: response = await send_whatsapp_flow(target_number, FLOW_ID_DO_SEU_CADASTRO_PUBLICADO, "Abrir Formulário de Cadastro") if response and response.get("status") == "success": return JSONResponse( status_code=status.HTTP_200_OK, content={"message": f"Flow de cadastro enviado com sucesso para {target_number}", "whatsapp_api_response": response["data"]} ) else: print(f"DEBUG: send_whatsapp_flow retornou erro: {response.get('message', 'Erro desconhecido')}") raise HTTPException( status_code=response.get("code", status.HTTP_500_INTERNAL_SERVER_ERROR), detail=f"Falha ao enviar Flow: {response.get('message', 'Erro desconhecido')}" ) except HTTPException: raise except Exception as e: print(f"❌ ERRO INESPERADO na trigger_cadastro_flow: {type(e).__name__}: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Erro interno ao disparar Flow: {e}" )