name: comercialrs-meta-webhook-button-update
description: Atualizar meta_webhook_vps.py para processar botões custom do template Meta nova_reuniao_criada — botões "Iniciada", "Concluída", "Remarketing" atualizam task via payload
category: devops
created: 2026-05-25
ComercialRS — Atualizar meta_webhook_vps.py para Botões Custom
Contexto
O que mudou
- - Template
nova_reuniao_criadaagora tem 3 botões custom (não é mais reply de texto): - - Não é broadcast — é enviado para um responsável específico
- - O
meta_webhook_vps.pyatual só processatype: "text"comcontext.id - - Button clicks chegam como
type: "button"combutton.payload= status direto - - Button clicks NÃO têm
context.id— não dá para fazer lookup por meta_message_id - - Recebe payload com
type: "button"ebutton.payload= status direto (in_progress,completed,remarketing) - - Sem
context.id→ lookup por(from_number, responsible_id)→ encontra task mais recente do responsável com statustodoouin_progress - - Atualiza task via
update_task_status_rpc - - Envia confirmação via
send_meta_reply - - Já está a gravar
task_idnomessage_logna funçãologMessageSent(linha 189:task_id: taskId) - - Já está a enviar para responsável específico em
sendToMetaRecipients - - Não precisa de alterações — a estrutura está pronta
- - Task: TESTE 2 - Reuniao com Hermes - IGNORAR
- - Status:
todo→in_progress✅ - - WhatsApp confirmação enviado e entregue ✅
- - HTTP 200 OK ✅
- - Log:
/var/log/meta_webhook.logcomloggingmodule (NAO print()) - -
/etc/nginx/snippets/meta-webhook-proxy.conf— proxy nginx (roteia tráfego) - -
/var/www/comercialrs/data/meta_webhook_server.py— código antigo, porta 8082, SEM botões - -
/var/www/comercialrs/data/meta_webhook_vps_service.py— código novo, porta 8083, COM botões - -
/var/log/meta_webhook.log— logs de AMBOS (compartilhado)
- in_progress → "Iniciada"
- completed → "Concluída"
- remarketing → "Remarketing"
O problema
O que precisa ser feito (2 ficheiros)
#### 1. /var/www/comercialrs/data/meta_webhook_vps.py
Lógica nova para button clicks:
Estrutura do payload Meta button click:
`json
{
"messages": [{
"from": "5511945678901",
"id": "wamid.xxx",
"type": "button",
"button": {
"payload": "in_progress", // ou "completed" ou "remarketing"
"text": "Iniciada"
}
}]
}
`
#### 2. /var/www/comercialrs/supabase/functions/dispatch-automation/index.ts
Ficheiro atualizado — meta_webhook_vps.py
O ficheiro /var/www/comercialrs/data/meta_webhook_vps.py precisa ser reescrito para:
1. Processar type: "button" — extrair button.payload como status
2. Para button clicks sem context.id: fazer lookup inverso
- Encontrar responsible_id pelo from_number (matching em public.team_members.phone)
- Encontrar task mais recente desse responsável com status todo ou in_progress
3. Manter compatibilidade com o fluxo existente de texto (replies com context.id)
4. Enviar confirmação com o label correto (Iniciada / Concluída / Remarketing)
Deployment — VPS access and commands used
SSH — use root, NOT rochasales
`bash
rochasales@31.97.243.106 — fails (password wrong for that user)
root@31.97.243.106 — works ✅
sshpass -p 'Marcia19671951@' ssh -o StrictHostKeyChecking=no root@31.97.243.106
`
Deploy commands (in order)
`bash
1. Kill existing process
sshpass -p 'Marcia19671951@' ssh -o StrictHostKeyChecking=no root@31.97.243.106 "fuser -k 8083/tcp"
2. Copy new file
sshpass -p 'Marcia19671951@' scp -o StrictHostKeyChecking=no meta_webhook_vps.py root@31.97.243.106:/var/www/comercialrs/data/meta_webhook_vps.py
3. Start
sshpass -p 'Marcia19671951@' ssh -o StrictHostKeyChecking=no root@31.97.243.106 "cd /var/www/comercialrs/data && nohup python3 meta_webhook_vps.py > /var/log/meta_webhook.log 2>&1 &"
4. Verify
sshpass -p 'Marcia19671951@' ssh -o StrictHostKeyChecking=no root@31.97.243.106 "fuser 8083/tcp && echo 'OK'"
5. Test webhook verification
curl "https://comercialrs.rochasalesseguros.com.br/meta-webhook/?hub.mode=subscribe&hub.verify_token=1c526598c56179f772e896d233badab&hub.challenge=test123"
Should return: test123
`
VERIFY_TOKEN — confirmed exact value
`
1c526598c56179f772e896d233badab
`
The file in VPS showed truncated value 1c5265...adab — extracted via head -15.
Verified working code
The file at /var/www/comercialrs/data/meta_webhook_vps.py deployed and confirmed working (2026-05-25, PID 810382).
Key findings during implementation
1. Button clicks arrive as type: "button" — not type: "text". The original code only handled text, completely ignoring button payloads.
2. button.payload IS the status directly — no keyword matching needed. Value is exactly in_progress, completed, or remarketing. The old parse_status() keyword approach is only needed for text replies.
3. Button clicks have NO context.id — they don't reference the original message, so message_log lookup by meta_message_id doesn't work. Must use fallback: phone → team_members.responsible_id → most recent open task (todo/in_progress).
4. team_members.phone comparison — use regexp_replace(phone, '\\D', '', 'g') LIKE %phone_clean% to handle various formats (with/without +, 55 prefix, spaces, etc.)
5. ⚠️ CRÍTICO — Button type reply vs custom: Meta WhatsApp templates suportam dois tipos de botão:
- custom: button.payload = valor definido no template (ex: in_progress, remarketing)
- reply: button.payload = o texto do botão (ex: "Iniciada", "Remarketing")
O template nova_reuniao_criada usa botões do tipo reply. Logs mostram:
`
button_payload="Iniciada" ← texto do botão, NÃO o payload
button_payload="Remarketing" ← texto do botão, NÃO o payload
`
Solução: adicionar textos também ao BUTTON_STATUSES e usar status_map:
`python
BUTTON_STATUSES = {'in_progress', 'completed', 'remarketing', 'Iniciada', 'Concluida', 'Remarketing'}
status_map = {
'in_progress': 'in_progress',
'completed': 'completed',
'remarketing': 'remarketing',
'Iniciada': 'in_progress', # texto do botão reply → status
'Concluida': 'completed', # texto do botão reply → status
'Remarketing': 'remarketing' # texto do botão reply → status
}
new_status = status_map.get(button_payload, button_payload)
`
Como verificar o tipo: inspecionar os logs em /var/log/meta_webhook.log:
`
[msg] from=5511913412661 type=button text="" button_payload="Iniciada"
[button] payload="Iniciada" text="Iniciada"
`
Se button_payload contém espaços e capitalização (ex: "Remarketing"), é reply. Se contém só underscore/lower (ex: "in_progress"), é custom.
CRÍTICO: Banco = Supabase Cloud (NAO Postgres Docker local)
Bug principal descoberto (trial-and-error)
1. Button clicado → find_task_by_phone_and_status retorna None → HTTP 200 nunca enviado → curl timeout
2. Causa raiz: webhook lia do Postgres Docker local (172.23.0.2) que tem dados diferentes do Supabase Cloud
3. Segundo problema: schema era auth em vez de public — queries não retornavam erro, só linha vazia
4. Terceiro problema: timestamp em incoming_messages é bigint — passar '' causa erro
Status — COMPLETED e VERIFICADO ✅
Botão clicado pelo Davi (5511913412661) com payload in_progress:
CRÍTICO: Dois servidores Python rodando simultaneamente
Em 28/05/2026 descobriu-se um problema grave de configuração:
`
Porta 8082: meta_webhook_server.py ← CÓDIGO ANTIGO, SEM handler de botões ❌
Porta 8083: meta_webhook_vps_service.py ← CÓDIGO NOVO, COM handler de botões ✅
Nginx: proxy_pass http://127.0.0.1:8082/ ← MANDANDO PARA O ANTIGO ❌
`
Sintoma: Botões clicados no WhatsApp não atualizavam tasks. Logs mostravam só heartbeats do código novo, zero button payloads.
Causa raiz: O nginx estava configurado para proxy para 8082 (código antigo) mas o arquivo atualizado rodava em 8083. O tráfego do WhatsApp nunca chegava ao código novo.
Fix aplicado (28/05/2026):
`bash
1. Mudar nginx proxy de 8082 → 8083
sed -i 's|proxy_pass http://127.0.0.1:8082/|proxy_pass http://127.0.0.1:8083/|' /etc/nginx/snippets/meta-webhook-proxy.conf
2. Validar e recarregar nginx
nginx -t && nginx -s reload
3. Parar serviço antigo (que ainda estava rodando via systemd)
systemctl stop meta-webhook.service
4. Verificar — só 8083 deve estar escutando
fuser 8081/tcp 8082/tcp 8083/tcp
Esperado: só 8083/tcp: XXXXXX
`
Verificação final:
`bash
Testar webhook verification — deve retornar "test123"
curl "https://comercialrs.rochasalesseguros.com.br/meta-webhook/?hub.mode=subscribe&hub.verify_token=1c526598c56179f772e896d233badab&hub.challenge=test123"
Ver logs — deve mostrar "Verificacao OK" + heartbeats do código novo
tail /var/log/meta_webhook.log
`
Como identificar o problema
Sebotões param de funcionar, verificar:
`bash
1. Quais portas estão escutando
fuser 8081/tcp 8082/tcp 8083/tcp
2. Qual código está rodando em cada porta
ps aux | grep meta_webhook | grep -v grep
3. Logs — código novo escreve "[meta-webhook-vps]" nos heartbeats
tail /var/log/meta_webhook.log
4. Nginx proxy config
cat /etc/nginx/snippets/meta-webhook-proxy.conf
Deve apontar para 8083, não 8082
`
Arquivos envolvidos
PROBLEMA: Botão atualiza reunião ERRADA (2026-06-01)
Sintoma
Corretor clica no botão de uma reunião específica, mas o feedback mostra outra reunião (título diferente).
Causa raiz
find_task_by_phone_and_status busca LIMIT 1 ordenado por due_date ASC — quando responsável tem múltiplas reuniões pendentes, o sistema sempre retorna a mais antiga (por vencimento), não a que o corretor clicou.
Solução implementada — Option A (2026-06-01)
Multi-task selection: quando botão é clicado e há 2+ reuniões pendentes, perguntar qual atualizar.
Fluxo:
1. Botão clicado → find_tasks_by_phone_and_status() (retorna TODAS, não LIMIT 1)
2. Se 1 tarefa → processa directo
3. Se 2+ tarefas → envia mensagem pedindo para escolher:
`
Qual reuniao voce quer marcar como Iniciada?
1 - Empresa ABCLtda (venc: 2026-06-05)
2 - Jose da Silva ME (venc: 2026-06-08)
Responda com o número (1, 2, etc.)
`
4. Corretor responde número → pending_selections[phone] guarda selecao pendente
5. Próxima mensagem do mesmo phone é validada como selecao numérica → actualiza tarefa correcta
Código novo:
`python
pending_selections = {} # top of file
find_tasks_by_phone_and_status returns list (not single task)
tasks = find_tasks_by_phone_and_status(msg_from, button_payload)
if len(tasks) == 1:
# direct update
update_task_status(task['task_id'], new_status)
else:
# save pending, ask which
pending_selections[msg_from] = {"status": new_status, "tasks": tasks, "ts": time.time()}
send_meta_reply(msg_from, "Qual reuniao voce quer marcar como X?\n1 - ...\nResponda com o número")
Text reply handler checks pending_selections first
if msg_type == 'text' and msg_text.strip().isdigit():
sel = pending_selections.get(msg_from)
if sel and 0 <= int(msg_text)-1 < len(sel['tasks']):
task = sel['tasks'][int(msg_text)-1]
update_task_status(task['task_id'], sel['status'])
del pending_selections[msg_from]
`
Verificar depois de editar:
`bash
python3 -c "import ast; ast.parse(open('/var/www/comercialrs/data/meta_webhook_vps.py').read()); print('OK')"
`
Reiniciar serviço:
`bash
fuser -k 8083/tcp && sleep 1 && nohup python3 /var/www/comercialrs/data/meta_webhook_vps_service.py > /var/www/comercialrs/data/meta_webhook_vps.log 2>&1 &
`