ComercialRS — Bug Fixes (2026-07-20)
Resumo da Sessão
Correção de bugs no webhook WhatsApp (meta_webhook_vps_service.py) e schema do banco Supabase Cloud.
Data: 2026-07-20
VPS: 31.97.243.106
Bugs Encontrados e Corrigidos
Bug #1: pending_selections inexistente no banco 🔴 CRÍTICO
Sintoma: Corretores com 2+ tarefas abertas não conseguiam concluí-las via WhatsApp. Sistema enviava menu mas falha silenciosa.
Causa raiz: O fluxo multi-step (quando há 2+ tarefas → menu numerado → escolha) usava:
1. Duas tabelas/funções que NÃO existiam no banco: pending_selections, get_pending_selection, set_pending_selection, clear_pending_selection
2. Um dicionário Python em memória _pending_selections = {} (thread-local, TTL 120s)
Impacto: Reinício do container = pendências perdidas. Corretores ficavam sem confirmação.
Bug #2: Keywords de texto livre incompletas 🟡 ALTO
Sintoma: Mensagens como "nk", "pronto", "encerrada", "feita" não eram reconhecidas.
Causa: Duplicados no dicionário (comecou 2x, concluida 2x) e variants comuns faltando.
Bug #3: Webhook quebrado 🔴 CRÍTICO
Sintoma: Serviço em crash loop (159.221 restarts!).
Causa raiz: O diretório /var/www/comercialrs/data/ não existia — o serviço apontava para um path que não existia no disco. Todas as tentativas de restart falhavam.
Correções Aplicadas
1. Schema criado no Supabase Cloud
`sql
-- Tabela
CREATE TABLE IF NOT EXISTS public.pending_selections (
id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
phone text NOT NULL,
selection_key text NOT NULL,
task_id uuid,
context jsonb DEFAULT '{}',
created_at timestamptz DEFAULT now(),
expires_at timestamptz DEFAULT (now() + interval '15 minutes')
);
-- Índice
CREATE INDEX IF NOT EXISTS idx_pending_selections_phone
ON public.pending_selections(phone);
-- Função: set_pending_selection
CREATE OR REPLACE FUNCTION public.set_pending_selection(
p_phone text, p_selection_key text, p_task_id uuid, p_context jsonb default '{}'
)
RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS '
BEGIN
DELETE FROM public.pending_selections WHERE phone = p_phone;
INSERT INTO public.pending_selections (phone, selection_key, task_id, context)
VALUES (p_phone, p_selection_key, p_task_id, p_context);
END;
';
-- Função: get_pending_selection
CREATE OR REPLACE FUNCTION public.get_pending_selection(p_phone text)
RETURNS TABLE(id uuid, phone text, selection_key text, task_id uuid, context jsonb, created_at timestamptz, expires_at timestamptz)
LANGUAGE plpgsql SECURITY DEFINER AS '
BEGIN
RETURN QUERY SELECT ps.id, ps.phone, ps.selection_key, ps.task_id, ps.context, ps.created_at, ps.expires_at
FROM public.pending_selections ps
WHERE ps.phone = p_phone AND ps.expires_at > now();
END;
';
-- Função: clear_pending_selection
CREATE OR REPLACE FUNCTION public.clear_pending_selection(p_phone text)
RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS '
BEGIN
DELETE FROM public.pending_selections WHERE phone = p_phone;
END;
';
`
2. Webhook atualizado (meta_webhook_vps_service.py)
Arquivo em produção: /var/www/comercialrs/data/meta_webhook_vps_service.py
Fonte editada: /tmp/comercialrs_build/data/meta_webhook_vps_service.py
Mudanças:
- -
_pending_selections = {}em memória → substituído por chamadas RPC ao banco - -
set_pending_selection()→ persiste no banco com TTL 15min - -
get_pending_selection()→ lê do banco, converte para formato original - -
clear_pending_selection()→ remove do banco - -
import threadingeimport timeremovidos (não mais necessários) - - Criado
/var/www/comercialrs/data/(não existia) - - Arquivo copiado para produção
- -
systemdreiniciado com sucesso - - Estado:
active (running) - - PID: 1360952
- - Porta: 8083
- - Arquivo:
/var/www/comercialrs/data/meta_webhook_vps_service.py - - Supabase Cloud:
dauftiqcvgaydddoxqhh.supabase.co - - DB Host:
db.dauftiqcvgaydddoxqhh.supabase.co - - DB Password:
tSm?p8n57f+TX5n - - Webhook verify token:
1c5265...adab - - Webhook porta: 8083
- - Webhook arquivo:
/var/www/comercialrs/data/meta_webhook_vps_service.py - - ANON_KEY:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InJvY2hhc2FsZXMiLCJyb2xlIjoiYW5vbiIsImlhdCI6MTc4MjE0MDAxMiwiZXhwIjoxODEzNjc2MDEyfQ.zSTyXFB2i9xsPJQ0rTCEhEL0JnMTj0brfExdqa85VWU
3. Keywords corrigidas
Antes:
`python
'in_progress': ['iniciada', 'comecou', 'comecou', 'andamento', 'em andamento', 'start', 'started'],
'completed': ['concluida', 'concluida', 'finalizada', 'done', 'completo', 'completa', 'fechada'],
'remarketing': ['remarketing', 'remar', 'rmk', 'remercar']
`
Depois:
`python
'in_progress': ['iniciada', 'comecou', 'andamento', 'em andamento', 'start', 'started', 'andando', 'progredindo', 'nk'],
'completed': ['concluida', 'finalizada', 'done', 'completo', 'completa', 'fechada', 'encerrada', 'feita', 'pronto', 'nk', ' pronta', 'concluido'],
'remarketing': ['remarketing', 'remar', 'rmk', 'remercar', 'revisitar', 'revisita']
`
4. Serviço reinstalado
Verificação do Sistema (2026-07-20)
Status do banco (Supabase Cloud)
`
status | count
-------------+-------
completed | 377
remarketing | 143
in_progress | 118
todo | 83
Total: 721 tarefas
Distribuição por mês:
2026-06: completed=107, in_progress=89, remarketing=60, todo=72
2026-05: completed=82, in_progress=1, remarketing=23, todo=10
2026-04: completed=176, in_progress=18, remarketing=55, todo=1
2026-03: completed=12, in_progress=10, remarketing=5
`
Dados saudáveis. Não há anomalia de colapso de completed.
Serviço webhook
Schema Completo do Banco (Supabase Cloud)
Tabela: public.tasks
| Coluna | Tipo | Notas |
| --- | --- | --- |
| id | uuid | PK |
| project_id | uuid | FK → projects |
| title | text | |
| description | text | |
| status | text | todo/in_progress/remarketing/completed |
| priority | text | |
| responsible_id | uuid | FK → team_members |
| start_date | timestamptz | |
| due_date | timestamptz | |
| progress | integer | 0-100 |
| created_by | uuid | |
| channel | text | whatsapp/web/etc |
| link | text | URL do negócio |
| updated_via | text | whatsapp/web |
| external_ref | text | |
| is_activity | boolean | 区分 reuniones vs actividades |
| task_order | integer | |
| deleted_at | timestamptz | Soft delete |
| dependencies | text | |
| updated_at | timestamptz | |
| Coluna | Tipo | Notas |
| --- | --- | --- |
| id | uuid | PK |
| name | text | ⚠️ NÃO é full_name |
| text | ||
| phone | text | |
| crm_email | text | Email no painel do corretor |
| avatar | text | |
| role | text | |
| created_by | uuid | |
| created_at | timestamptz | |
| updated_at | timestamptz | |
| Coluna | Tipo | Notas |
| --- | --- | --- |
| id | uuid | PK |
| meta_message_id | text | ID da mensagem Meta |
| task_id | uuid | FK → tasks |
| responsible_id | uuid | FK → team_members |
| template_name | text | Nome do template |
| phone | text | |
| status | text | |
| created_at | timestamptz | |
| updated_at | timestamptz | |
| Coluna | Tipo | Notas |
| --- | --- | --- |
| id | uuid | PK |
| message_id | text | Meta message ID |
| from_number | text | Phone |
| timestamp | bigint | Unix timestamp |
| msg_type | text | button/text |
| body | text | Texto da resposta |
| parsed_status | text | in_progress/completed/etc |
| task_id | uuid | FK → tasks |
| created_at | timestamptz | |
| Coluna | Tipo | Notas |
| --- | --- | --- |
| id | uuid | PK |
| phone | text | Phone do corretor |
| selection_key | text | Status destino (completed/etc) |
| task_id | uuid | Task selecionada |
| context | jsonb | Array de tasks para o menu |
| created_at | timestamptz | |
| expires_at | timestamptz | TTL 15 minutos |
| Arquivo | Importância | Notas |
| --- | --- | --- |
/var/www/comercialrs/data/meta_webhook_vps_service.py | 🔴 PRODUÇÃO | Webhook WhatsApp ativo |
/tmp/comercialrs_build/data/meta_webhook_vps_service.py | 🟡 FONTE | Editar aqui, copiar pra produção |
/var/www/comercialrs/data/ | 🟡 CRÍTICO | Diretório DEVE existir |
/var/www/comercialrs/ | 🟢 BUILD | Build compilado (não source) |
/tmp/cr_build_root/data/meta_webhook_vps_service.py | 🟢 BACKUP | Versão Jun 10 (antiga) |
Fluxo do Webhook (como funciona)
`
WhatsApp message incoming (POST /webhook)
↓
Button click?
├─ NO → Text reply
│ ├─ has pending selection? → apply_status_from_selection()
│ └─ no pending → get_task_by_meta_message_id() [via message_log]
│ ├─ task found → update_task_status()
│ └─ task not found → "nenhuma reunião ativa"
└─ YES → parse_button_payload()
├─ has task_id in payload? → direct update (NOVO FORMATO)
├─ pending selection active? → apply_status_from_selection()
├─ 0 open tasks? → "nenhuma reunião ativa"
├─ 1 open task? → direct update
└─ 2+ open tasks? → send menu → set_pending_selection() [BANCO]
`
Pendente: Edge Function dispatch-automation (Bug #2)
O message_log.task_id=null reported no handoff pode estar na Edge Function dispatch-automation (Supabase Cloud), não no webhook.
Onde verificar: Supabase Dashboard → Edge Functions → dispatch-automation
O que fazer:
1. Verificar se a Edge Function cria message_log entries com task_id null
2. Se sim, adicionar task_id ao payload quando dispatching templates de botão
3. Garantir que meta_message_id é passado corretamente