Cronjob + Cloud Functions pra enviar newsletter do TabNews automaticamente
Eu gosto muito das newsletter do tabnews, me ajudam a estar atualizado sobre as notícias de tecnologias do mundo! Mas eu tinha um problema, que geralmente não incomoda ninguém mas a mim estava chato: queria acompanhar as newsletters do TabNews, mas não queria ficar abrindo email..
Pensei: seria legal se as newsletters chegassem como notificação no celular, igual outras notificações de app. Aí eu vejo na hora, decido se quero ler, e se quiser, já abro direto no conteúdo.
E enquanto eu pensava em uma solução.. scrapping de email, webhook, RSS, um bot, etc eu notei que as newsletters eram postadas, individualmente, aqui no TabNews pelo perfil @NewsletterOficial. Perfeito, solução encontrada! Vou monitorar esse perfil pela API e enviar um push notificaction pro meu device (claro, precisa de um app pra isso funcionar).
Mas pra isso tudo acontecer, precisava de algo rodando em algum lugar que:
- Checasse periodicamente se saiu newsletter nova
- Quando saísse, mandasse push notification pro meu celular
- Rodasse sozinho, sem eu precisar ficar gerenciando servidor
Foi aí que entrei no mundo de Cloud Functions e cronjobs.
Como funciona?
Pra quem não conhece, Cloud Function é basicamente código que roda na nuvem sem você precisar subir servidor. Você escreve uma função JavaScript (ou Python, Go, etc), faz deploy, e pronto. A infraestrutura toda é gerenciada automaticamente. E cronjob é só uma tarefa agendada, tipo "roda essa função todo dia às 18h" ou "roda a cada hora".
Juntando os dois, você consegue ter código rodando periodicamente na nuvem, checando coisas, sem custo de servidor fixo. Você paga só quando a função executa.
Decidi usar Firebase porque já tinha um projeto rodando lá. O Firebase tem Cloud Functions integradas e o Cloud Scheduler (que é o serviço de cronjob deles) é bem simples de configurar. E o Firebase Cloud Messaging (FCM) é gratuito pra push notifications.
Comecei simples: fiz uma função que roda de hora em hora, busca a API do TabNews pra ver os últimos posts do @NewsletterOficial, compara com o último post salvo, e se for um post novo, manda push pro meu celular. O "comparar com o último post" funciona checando o ID do último post pelo GET da API com o último ID do post que eu salvei no banco. Se for diferente, é porque saiu newsletter nova. Aí eu salvo o novo ID e mando a notificação.
Pra salvar esse "último ID checado", usei o Firestore (que é o banco NoSQL do Firebase). Criei um documento bem simples que guarda só o postId da última newsletter notificada. Antes de enviar qualquer notificação, atualizo esse documento com o novo ID.
O cronjob ficou configurado pra rodar de hora em hora, das 8h às 20h. Escolhi esse intervalo porque newsletter não tem horário fixo de publicação, então precisa checar ao longo do dia. E de hora em hora é suficiente, não precisa ficar martelando a API a cada minuto.
sequenceDiagram
participant Scheduler as Cloud Scheduler
participant Function as Cloud Function
participant TabNews as API TabNews
participant Firestore as Firestore
participant FCM as Firebase Messaging
participant Celular as iPhone
Scheduler->>Function: Dispara (a cada hora)
Function->>TabNews: GET /api/v1/contents/NewsletterOficial
TabNews-->>Function: Lista de posts
Function->>Firestore: Qual foi o último ID notificado?
Firestore-->>Function: abc123
alt Newsletter nova detectada
Function->>Firestore: Salva novo ID (xyz789)
Function->>FCM: Envia push notification
FCM-->>Celular: "Nova newsletter no TabNews!"
else Nenhuma novidade
Function-->>Scheduler: Finaliza (não faz nada)
end
Testei por alguns dias e funcionou. Toda vez que o @NewsletterOficial publicava, em no máximo 1 hora eu recebia a notificação no celular. Bem melhor que email, onde eles esperam sumarizar conteúdos para enviar.
Outras funções
Se funciona pra newsletter, funciona pra outras coisas também. Fiz uma segunda função pra me notificar sobre o post mais relevante do dia. Essa roda só uma vez por dia, às 18h, e usa a strategy=relevant da API do TabNews. Ela pega o post mais relevante do momento, filtra pra garantir que tem pelo menos 5 tabcoins (pra não notificar post sem engajamento), e manda push.
E fiz uma terceira pra digest semanal. Usando a mesma técnica de monitorar um usuário. O italosouza criou o usuário @TabNewsDigest, um digest semanal com os melhores posts da semana, onde posta todo sábado por volta do meio-dia, então configurei pra rodar sábado às 11:30 e às 13:00. Com duas checagens eu garanto que pego o digest logo depois que sair.
Nada disso é mágico. É só polling simples: busca a API, compara ID, se mudou, notifica. A "inteligência" toda está na API do TabNews, que já retorna os posts ordenados por relevância ou data. Eu só consumo isso!
O tier gratuito do Firebase aguenta tranquilo (são poucos requests por dia), e não tem custo de servidor porque Cloud Function só cobra quando executa.
Isso tudo começou com "quero receber newsletter por push", mas acabou virando uma coisa maior. Eventualmente apliquei todo esse conhecimento no app iOS do TabNews que estava desenvolvendo. As mesmas Cloud Functions que me mandam notificação pessoal agora mandam pra todos os usuários do app. Quando alguém baixa o app e permite notificações, o token desse device é salvo no Firestore na collection tabnews_deviceTokens, e entra no loop (quase um broadcast) de envio.
Na notificação eu mando alguns dados extras (postId, slug, owner, type) pra fazer deep linking. Quando o usuário toca na notificação, o app abre direto no post certo e na aba certa (Newsletter, Relevante ou Digest). Fica bem fluido.
E dentro do app também tem um "Daily Briefing": todo dia às 18h aparece um banner com os 5 posts mais relevantes do dia. Esse é calculado localmente no app (usando um score simples: tabcoins + comentários × 2), não depende das Cloud Functions.
Stack completa que acabei usando:
- Firebase Cloud Functions (Node.js)
- Cloud Scheduler pra cronjobs
- Firestore pra salvar estado
- Firebase Cloud Messaging pra push
- Swift/SwiftUI no app iOS
- API pública do TabNews
Se alguém tiver curiosidade sobre Cloud Functions, cronjobs, push notification, arquitetura serverless ou só Swift/SwiftUI só chamar. É uma stack bem útil pra projetos pessoais porque o custo é praticamente zero (se o volume for baixo)!
App do TabNews (não oficial): https://apps.apple.com/us/app/tabnews-reader/id6755933359?l=pt-BR
E uma coisa que apliquei nesse projeto todo foi a Inovação incremental, onde eu pensei primeiro no problema que eu vejo e e como eu resolveria (e se resolveria para outras pessoas também!). Fiz um post exatamente sobre isso!