Radar de Vagas
Candidatura em vaga de tecnologia é um jogo de latência. Este radar fecha a janela entre a publicação e o aviso para 30 minutos.
Meu papel
Projeto e implementação, da medição do problema ao workflow em produção
O problema
Analisei meu próprio histórico de candidaturas na Gupy procurando o que separava as que avançaram das que não. O perfil não era o gargalo — a janela era. Quatro horas depois da publicação, uma vaga tinha 7 propostas concorrentes; dezoito horas depois, 67.
Descobrir a vaga abrindo o site significa entrar já na faixa ruim da curva. Não é um problema de escrever currículo melhor: é de chegar antes.
A solução
Um coletor bate na API pública da Gupy a cada 30 minutos, filtra e pontua as vagas contra quatro perfis de busca, e grava as novas no Postgres. O que ainda não foi avisado sai no WhatsApp pela Evolution API.
A orquestração é um workflow de n8n de 11 nós, e o coletor em Python usa só a biblioteca padrão — sem dependência para instalar. Todo o stack sobe com um docker compose, em portas próprias para não colidir com outros projetos na mesma máquina.
O detalhe que sustenta o resto: o estado da notificação mora numa coluna, não no INSERT. Uma falha de entrega no WhatsApp devolve a vaga ao ciclo seguinte em vez de queimá-la em silêncio.
Como funciona
- 01
Cron com jitter
O agendamento roda aos minutos 7 e 37 em vez de 0 e 30. São 192 requisições por dia num endpoint que qualquer navegador chama — o que chama atenção é regularidade de relógio, não volume.
- 02
Coleta por perfil
Quatro querystrings com filtro no servidor cobrem estágio e júnior, remoto e presencial. O loop pagina enquanto a resposta vier cheia e espera 2s entre perfis.
- 03
Filtro e pontuação
A comparação normaliza acento e olha só o título da vaga. Cada vaga recebe uma pontuação de aderência antes de entrar no banco.
- 04
Gravação idempotente
A carga entra por COPY numa tabela temporária e daí para a tabela final, com ON CONFLICT DO NOTHING sobre a chave composta de fonte e id externo.
- 05
Notificação do que ficou pendente
Um SELECT pega o que ainda não foi avisado, monta a mensagem e envia pela Evolution. Só depois do envio bem-sucedido a vaga é carimbada.
- 06
Vigia do vigia
Um Error Trigger avisa quando o radar quebra e um heartbeat diário informa quantas vagas passaram nas últimas 24h. Sem ele, nenhuma vaga nova hoje e o radar morreu na terça produzem o mesmo silêncio.
Decisões técnicas
Medir a API antes de escrever o coletor
O plano original usava 18 termos de busca por palavra-chave. Medindo contra a API ao vivo, 6 deles retornavam zero e uma vaga de Data Engineer júnior remoto era invisível para todos. Troquei por 4 querystrings com filtro no servidor: a cobertura foi de 117 para cerca de 1.716 vagas.
Filtrar pelo título, não pela descrição
Filtrar por descrição parecia mais abrangente e não era: 47% de falso positivo em dados e 30% de falso negativo em suporte. A descrição menciona o mundo inteiro; o título é o que a vaga realmente é.
COPY em vez de INSERT montado por string
Títulos vêm com aspas, vírgulas e emoji direto de uma API pública. A carga vai para uma tabela temporária via CSV e daí para a tabela final — concatenar isso em SQL seria injeção esperando acontecer.
Paginar pelo tamanho da página, não pelo total
O total informado pela API satura no tamanho do limite: um perfil com 1.370 vagas responde total de 100. A paginação segue enquanto a resposta vier cheia, e não enquanto faltar chegar ao total.
Os dois caminhos do filtro provam paridade
O filtro existe em Python, no coletor, e em JavaScript, dentro do n8n. Sobre as mesmas 253 vagas reais os dois aprovam exatamente as mesmas vagas com os mesmos scores — testado, não presumido.
Segredo fora do git desde o commit inicial
O gitignore foi o primeiro arquivo versionado, antes de qualquer credencial existir. A chave da Evolution entra no n8n como credencial, nunca como header literal — o export do workflow grava header literal em texto puro. O telefone mora numa tabela de configuração: chave se rotaciona, telefone não.
Bugs que só apareceram rodando de verdade
O terminal do Windows estourava em título com emoji, 7 em cada 253 vagas. E o WhatsApp só fecha o asterisco de negrito quando ele está colado a um caractere não-branco, então título que termina em espaço exibia os asteriscos crus. Nenhum dos dois sai de ler o código.
O que o sistema faz
- Avisa no WhatsApp em até 30 minutos da publicação
- Cobre quatro perfis de busca, remoto e presencial
- Pontua a aderência de cada vaga antes de notificar
- Não repete vaga já enviada, mesmo após falha de entrega
- Avisa sozinho quando o próprio radar quebra
- Heartbeat diário para distinguir silêncio de defeito
- Coletor com cinco modos, incluindo ensaio sem gravar nem enviar
- Sobe inteiro com um docker compose, em portas próprias