Painel de atendimento¶
O painel FastAPI em src/emulator/app.py utiliza HTML renderizado por Jinja2 e atualizações parciais. Ele tem autenticação própria por WhatsApp e permanece disponível quando DEV_UI_ENABLED=false. A dashboard de desenvolvimento é outra superfície: ela é bloqueada por essa flag.
Uso pelo operador¶
Após entrar pelo WhatsApp, abra /atendimento. A lista reúne todos os pendentes e atendimentos em andamento, com área, nome, responsável, resumo e horário. Ao selecionar uma conversa, o painel carrega o histórico daquela sessão, o resumo operacional e o motivo do handoff.
Assumir muda HANDOFF_PENDING para HUMAN. Enviar também assume uma pendência automaticamente. Finalizar fecha o atendimento, remove-o da lista e, quando CRM está habilitado, informa que a sincronização está pendente. O serviço aceita finalizar tanto HUMAN quanto HANDOFF_PENDING, embora a interface apresente a ação de finalização para conversas HUMAN.
assigned_to é informativo, não bloqueia colaboração. A lista não consulta histórico por linha: usa duas consultas por estado; somente a conversa selecionada carrega mensagens. O nome vem de cadastral_data.nome, de recognized_name ou, como fallback, do telefone.
Contratos HTTP¶
Os POSTs recebem application/x-www-form-urlencoded; não são endpoints de JSON. Requisições autenticadas enviam o cookie gogenetic_operator_session. Tokens dos exemplos são fictícios.
| Método / endpoint | Entrada | Sucesso | Erros relevantes |
|---|---|---|---|
GET /atendimento/acesso/{token} |
Token no path | 200 HTML de confirmação; não consome o token | A validade é conferida no POST |
POST /atendimento/acesso |
token |
303 para /atendimento, com cookie |
400 expirado/consumido; 403 removido da whitelist |
GET /atendimento |
telefone, erro, aviso opcionais |
200 HTML; sem sessão mostra tela de acesso | Falhas de armazenamento podem impedir carregamento |
GET /atendimento/lista |
telefone opcional |
Fragmento HTML da fila | 401 e HX-Redirect: /atendimento |
GET /atendimento/mensagens |
telefone obrigatório |
HTML e headers X-Conversation-State, X-Window-Open |
401 sem acesso; 404 conversa fora da fila |
POST /atendimento/assumir |
telefone, csrf_token |
303 para conversa | 401 sessão; 403 CSRF; erro de domínio redireciona com erro |
POST /atendimento/enviar |
telefone, content, csrf_token |
303 após envio | Mesma autenticação; janela, conteúdo e concorrência viram aviso no redirecionamento |
POST /atendimento/finalizar |
telefone, csrf_token |
303 com aviso de encerramento | Mudança concorrente ou envio em andamento |
POST /atendimento/sair |
csrf_token |
Revoga sessão, apaga cookie e redireciona | 401/403 |
GET /atendimento/crm |
Cookie | HTML com PENDING, BLOCKED, UNCERTAIN |
401; não oferece mutação de CRM |
Exemplo de corpo de envio, para inspecionar no navegador em homologação:
POST /atendimento/enviar
Content-Type: application/x-www-form-urlencoded
Cookie: gogenetic_operator_session=<TOKEN_DE_SESSAO>
telefone=%2B5500000000000&content=Mensagem+ficticia+de+homologacao&csrf_token=<CSRF>
Sucesso retorna 303 e Location: /atendimento?telefone=.... O conteúdo da conversa não é devolvido como Message JSON. Campos obrigatórios ausentes recebem a validação padrão FastAPI, geralmente HTTP 422.
Envio, janela e falhas parciais¶
send_human_message() limpa espaços externos, rejeita texto vazio e limita o conteúdo a 4.096 caracteres. Exige um inbound há menos de 24 horas; exatamente 24 horas já está fora da janela. O serviço consulta até 200 mensagens do telefone para essa validação; a tela usa o histórico da sessão selecionada. Essa diferença deve ser considerada ao investigar casos de sessão antiga.
Depois de assumir, adquire lock condicional de 90 segundos, envia pelo adapter WhatsApp, registra outbound com operator_phone, agent=None e provider_message_id, e libera o lock em finally. Finalizar durante um envio com lock válido é bloqueado.
Se WhatsApp falhar, o atendimento permanece HUMAN e não há registro outbound de sucesso. Se WhatsApp tiver sucesso mas a persistência do histórico falhar, o resultado é transcript_persisted=false; a tela avisa “Não reenvie”. O cliente já recebeu a mensagem. Use human_message_transcript_persist_failed e o ID do provider para investigar sem duplicar envio. Falha ao liberar lock também não transforma um envio entregue em erro repetível; o lease expira.
Auditoria e CRM¶
As ações geram eventos operator.assume, operator.send e operator.finalize; o evento de envio inclui IDs, sem copiar seu conteúdo. operator_audit_write_failed indica falha secundária de auditoria.
/atendimento/crm preserva a visibilidade de problemas inclusive de sessões já encerradas. PENDING é trabalho a processar; BLOCKED exige intervenção; UNCERTAIN exige conferir se o negócio já foi criado antes de qualquer repetição. Veja troubleshooting e integração CRM.
Testes e alteração¶
Execute python -m pytest tests/unit/application/test_human_conversation.py tests/unit/application/test_operator_auth.py tests/unit/lambdas/test_emulator.py -q. Os testes cobrem colaboração, acesso de uso único, cookie seguro, CSRF, janela fechada, concorrência de envio e finalização, e entrega com falha de histórico. Ao alterar formulários, preserve nomes e tokens; ao alterar envio, preserve a distinção entre falha antes e depois da entrega externa.