12 Dias de DigitalOcean (Dia 4) – Implantando Notificações de Aniversário com Funções da DigitalOcean

Bem-vindo ao Dia 4 dos 12 Dias do DigitalOcean! Ontem, adicionamos notificações por SMS do Twilio ao nosso Serviço de Lembrete de Aniversário, tornando-o capaz de enviar mensagens de texto para os aniversários de hoje. 🎂

Hoje, levaremos as coisas para o próximo nível implantando nosso script no DigitalOcean Functions. Isso permite que nosso serviço funcione na nuvem sem a necessidade de um servidor dedicado, tornando nosso aplicativo leve, escalável e pronto para automação.

Com essa configuração, você receberá lembretes de aniversário mesmo quando seu computador estiver desligado ou não conectado à internet—não há mais necessidade de executar o script manualmente na sua máquina. 🎉

Por que DigitalOcean Functions?

Às vezes, tudo que você precisa é de um script simples que roda ocasionalmente. Gerenciar a infraestrutura para algo assim pode ser um exagero. É aí que Functions entra. É uma plataforma sem servidor, o que significa que você pode implantar código que roda apenas quando necessário, e você paga apenas pelo que usa. Perfeito para nosso caso de uso—verificar aniversários e enviar lembretes diariamente.

🚀 O Que Você Vai Aprender

No final de hoje, você saberá como:

  1. Configurar a ferramenta doctl CLI da DigitalOcean.
  2. Criar e conectar-se a um namespace sem servidor (a forma da DigitalOcean de manter funções organizadas).
  3. Empacotar e implantar seu Serviço de Lembrete de Aniversário para DigitalOcean Functions.
  4. Testar sua função implantada na nuvem.

🛠 O Que Você Vai Precisar

Antes de começar, certifique-se de ter:

🧑‍🍳 Receita para o Dia 4: Implementando as Funções do DigitalOcean

Passo 1: Configurar o doctl CLI

Se você já configurou o doctl em sua máquina, pode pular esta etapa. Para aqueles que precisam configurá-lo, sigam estas instruções:

Antes de começarmos, vamos falar rapidamente sobre doctl. É a ferramenta oficial de interface de linha de comando da DigitalOcean que permite gerenciar seus recursos em nuvem diretamente do seu terminal. Usaremos para criar um namespace (uma pasta para nossas funções serverless), implantar nosso script em Python e testar a função.

Configurar é simples:

  1. Instale doctl: Siga o guia de instalação para o seu sistema operacional.

  2. Autentique doctl: Conecte-o à sua conta DigitalOcean executando:

    doctl auth init
    
  3. Verifique a instalação: Certifique-se de que tudo está funcionando executando:

    doctl account get
    

Se for bem-sucedido, este comando retornará detalhes sobre sua conta DigitalOcean, como seu e-mail e ID da conta.

Passo 2: Instale o Software Serverless

As Funções DigitalOcean requerem software de suporte serverless, que você precisará instalar. Esta é uma configuração única, então uma vez instalado, você não precisará fazer isso novamente para projetos futuros.

Execute o seguinte comando:

doctl serverless install

Você pode verificar o status da instalação com:

doctl serverless status

Se você vir um erro como:

Error: serverless support is installed but not connected to a functions namespace

Não se preocupe—isso apenas significa que ainda não criamos ou nos conectamos a um namespace. Nós vamos lidar com isso no próximo passo.

Passo 3: Crie e Conecte-se a um Namespace

Os namespaces são como pastas para organizar funções sem servidor. Vamos criar um para o nosso Serviço de Lembrete de Aniversário:

  1. Criar um novo namespace:

    doctl serverless namespaces create --label "meu-namespace-lembrete-aniversario" --region "nyc1"
    

  2. Conectar ao namespace:

    doctl serverless connect meu-lembrete-aniversario-namespace
    

  3. Verificar a conexão:

    doctl serverless status
    

Você deve agora ver uma confirmação de que está conectado ao namespace.

Dica Profissional: Para ver uma lista de todos os namespaces disponíveis, use o seguinte comando:

doctl serverless namespaces list

Isso pode ser útil se você estiver gerenciando vários projetos ou quiser verificar o namespace que acabou de criar.

Passo 4: Inicializar e Configurar a Estrutura do Projeto

O DigitalOcean Functions espera uma estrutura de projeto específica para implantações serverless. Você pode iniciar essa estrutura usando doctl serverless init, criá-la manualmente ou até mesmo clonar um repositório inicial. Para manter as coisas simples, vamos configurá-la usando doctl serverless init:

  1. Execute o seguinte comando para inicializar o projeto:

    doctl serverless init --language python birthday-reminder-service
    

    Isso cria um diretório de projeto local chamado my-birthday-reminder-service com a seguinte estrutura padrão:

    my-birthday-reminder-service/
    ├── packages
    │   └── sample
    │       └── hello
    │           └── hello.py
    └── project.yml
    

  2. Navegue até o diretório do projeto:

    cd meu-lembrador-de-aniversario
    
  3. Renomeie as pastas para corresponder ao nosso caso de uso:

    mv pacotes/exemplo pacotes/lembranças
    mv pacotes/lembranças/olá pacotes/lembranças/aniversários
    mv pacotes/lembranças/aniversários/olá.py pacotes/lembranças/aniversários/__main__.py
    
  4. Crie os arquivos necessários:

    • Crie um arquivo .env vazio na raiz do projeto:
    touch .env
    

    Este arquivo conterá suas credenciais do banco de dados e do Twilio. O arquivo estará localizado na raiz da pasta my-birthday-reminder-service.

    • Crie um arquivo requirements.txt na pasta birthdays:
    touch packages/reminders/birthdays/requirements.txt
    

    Este arquivo listará as dependências do Python necessárias para a sua função. Ele estará localizado em packages/reminders/birthdays.

    • Crie um arquivo build.sh na pasta birthdays:
    touch packages/reminders/birthdays/build.sh
    chmod +x packages/reminders/birthdays/build.sh
    

    O script build.sh é necessário para implantar funções com dependências externas. O comando chmod garante que o script seja executável em sistemas Mac/Linux.

Estrutura Atualizada: Após concluir essas etapas, a estrutura do seu projeto deve ser parecida com isto:

my-birthday-reminder-service/
├── project.yml
├── .env
├── packages
│   └── reminders
│       └── birthdays
│           ├── __main__.py
│           ├── requirements.txt
│           ├── build.sh
├── .gitignore

Dica Profissional: Se você acidentalmente renomear uma pasta de forma incorreta, pode executar o comando novamente ou renomeá-la manualmente em seu explorador de arquivos.

Passo 5: Atualizar Arquivos

Agora que a estrutura está em vigor, vamos preenchê-la com os arquivos necessários. Abra o diretório my-birthday-reminder-service em seu editor de código favorito

1. Atualize project.yml

O arquivo project.yml é um arquivo de configuração que define a estrutura do seu projeto serverless, variáveis de ambiente e funções. Substitua seu conteúdo por:

packages:
  - name: reminders
    shared: false
    environment:
      DO_DB_NAME: "${DB_NAME}"
      DO_DB_USER: "${DB_USER}"
      DO_DB_PASSWORD: "${DB_PASSWORD}"
      DO_DB_HOST: "${DB_HOST}"
      DO_DB_PORT: "${DB_PORT}"
      TWILIO_ACCOUNT_SID: "${TWILIO_ACCOUNT_SID}"
      TWILIO_AUTH_TOKEN: "${TWILIO_AUTH_TOKEN}"
      TWILIO_PHONE_FROM: "${TWILIO_PHONE_FROM}"
      TWILIO_PHONE_TO: "${TWILIO_PHONE_TO}"
    functions:
      - name: birthdays
        runtime: python:default

Este arquivo configura o pacote de lembretes e mapeia as variáveis de ambiente para as Funções da DigitalOcean. Cada variável corresponde às credenciais necessárias para seu banco de dados e integração com Twilio.

2. Atualize seu arquivo .env

Consulte Dia 1: Configurando um Banco de Dados PostgreSQL para Lembretes de Aniversário para as credenciais do banco de dados e Dia 3: Verificando Aniversários e Enviando Notificações SMS para as credenciais do Twilio para preencher os seguintes valores:

# Credenciais do banco de dados (do Dia 1)
DB_HOST=<your-database-hostname>
DB_NAME=<your-database-name>
DB_USER=<your-database-username>
DB_PASSWORD=<your-database-password>
DB_PORT=5432  # Porta padrão do PostgreSQL

# Credenciais do Twilio (do Dia 3)
TWILIO_ACCOUNT_SID=<your-twilio-account-sid>
TWILIO_AUTH_TOKEN=<your-twilio-auth-token>
TWILIO_PHONE_FROM=<your-twilio-phone-number>
TWILIO_PHONE_TO=<your-personal-phone-number>

Nota: O arquivo .env é usado para armazenar credenciais sensíveis de forma segura. Esses valores serão lidos pelo seu arquivo project.yml e mapeados para o ambiente serverless durante a implantação, tornando-os acessíveis à sua função na nuvem.

3. Adicionar Dependências

Atualize o arquivo requirements.txt com as seguintes dependências:

pg8000  
python-dotenv  
twilio  

pg8000: Uma biblioteca cliente PostgreSQL em Python puro.

python-dotenv: Usado para carregar variáveis de ambiente do arquivo .env.

twilio: A biblioteca Twilio para Python para enviar mensagens SMS.

4. Atualizar build.sh

Adicione o seguinte script ao arquivo build.sh:

#!/bin/bash
set -e

# Imprimir o diretório de trabalho atual para depuração
echo "Current working directory: $(pwd)"

# Verificar se o requirements.txt existe
if [[ -f "requirements.txt" ]]; then
  echo "Found requirements.txt in $(pwd)"
else
  echo "Error: requirements.txt not found in $(pwd)"
  exit 1
fi

# Criar um ambiente virtual
virtualenv --without-pip virtualenv

# Instalar dependências do requirements.txt
pip install -r requirements.txt --target virtualenv/lib/python3.9/site-packages

Este script garante que todas as dependências sejam empacotadas corretamente com sua função. O comando chmod +x do Passo 4 garante que ele seja executável.

5. Atualizar __main__.py

Este é o script principal para o seu Serviço de Lembrete de Aniversário. Estamos essencialmente usando o script que construímos no Dia 3 para enviar notificações de aniversário. No entanto, para torná-lo compatível com as Funções do DigitalOcean, precisamos fazer alguns pequenos ajustes.

Atualize o arquivo __main__.py com o seguinte conteúdo:

# serviço_de_lembrete_de_aniversário/__main__.py

from datetime import datetime
import pg8000
from dotenv import load_dotenv
from twilio.rest import Client
import os

# Carregar variáveis de ambiente
load_dotenv()

def main(params):
    """DigitalOcean Functions entry point."""
    try:
        # Conectar ao banco de dados
        connection = pg8000.connect(
            host=os.getenv("DO_DB_HOST"),
            database=os.getenv("DO_DB_NAME"),
            user=os.getenv("DO_DB_USER"),
            password=os.getenv("DO_DB_PASSWORD"),
            port=int(os.getenv("DO_DB_PORT"))
        )
        cursor = connection.cursor()

        # Obter o mês e o dia de hoje
        today = datetime.now()
        today_month = today.month
        today_day = today.day

        # Consulta para buscar contatos cujo aniversário coincide com a data de hoje
        cursor.execute(
            """
            SELECT first_name, last_name, birthday
            FROM contacts
            WHERE EXTRACT(MONTH FROM birthday) = %s
              AND EXTRACT(DAY FROM birthday) = %s;
            """,
            (today_month, today_day)
        )
        rows = cursor.fetchall()

        # Notificar para cada contato correspondente
        if rows:
            account_sid = os.getenv("TWILIO_ACCOUNT_SID")
            auth_token = os.getenv("TWILIO_AUTH_TOKEN")
            client = Client(account_sid, auth_token)

            for row in rows:
                first_name, last_name, _ = row
                message = client.messages.create(
                    body=f"🎉 It's {first_name} {last_name or ''}'s birthday today! 🎂",
                    from_=os.getenv("TWILIO_PHONE_FROM"),
                    to=os.getenv("TWILIO_PHONE_TO")
                )
                print(f"Message sent for {first_name} {last_name}. Message SID: {message.sid}")
        else:
            print("No birthdays today.")

        # Fechar o cursor e a conexão
        cursor.close()
        connection.close()

    except Exception as e:
        print(f"An error occurred: {e}")

Aqui está o que mudamos:

  1. Adicionada uma função main(params): As Funções do DigitalOcean esperam uma função de ponto de entrada chamada main, que aceita um argumento params. É aqui que a função começa a execução.

  2. Movida a lógica do script dentro da função main:
    O código do Dia 3 foi encapsulado dentro da função main para se alinhar a esse requisito.

  3. Tudo o mais permanece o mesmo:
    A lógica de conexão com o banco de dados, verificações de aniversário e lógica de notificação por SMS permanecem inalteradas.

Passo 5: Empacotar e Implementar

Com tudo em ordem, implemente seu projeto nas Funções da DigitalOcean:

  1. Implemente o projeto:
doctl serverless deploy my-birthday-reminder-service

Para verificar se sua função foi implementada com sucesso no namespace:

  1. Acesse o Painel de Controle da DigitalOcean e vá para Funções no menu do lado esquerdo.
  2. Localize seu namespace (por exemplo, meu-namespace-de-lembrete-de-aniversario).
  3. Verifique se sua função aparece no namespace, geralmente listada como lembretes/aniversarios.
  4. Clique no nome da função para ver detalhes, incluindo logs, configuração e histórico de invocações.

Passo 6: Teste Sua Função Implementada

Depois de implantar sua função, é hora de testá-la. Você pode invocar a função manualmente para garantir que ela funcione conforme o esperado. Existem duas maneiras de fazer isso:

Opção 1: Usando a CLI do DigitalOcean

doctl serverless functions invoke reminders/birthdays

Se tudo estiver configurado corretamente, sua função será executada na nuvem, verificando os aniversários de hoje e enviando notificações por SMS.

![https://doimages.nyc3.cdn.digitaloceanspaces.com/006Community/12-Days-of-DO/Postgressql-birthday/birthday_reminder_service_text_message.jpeg]

Opção 2: Usando o Painel de Controle do DigitalOcean

  1. Acesse o Painel de Controle do DigitalOcean.
  2. Navegue até Funções e localize sua função de lembretes/aniversários.
  3. Clique em Executar para executá-la manualmente.
  4. Visualize a saída e os logs diretamente no console.

Este método é especialmente útil se você prefere uma interface visual ou deseja verificar os logs em um formato limpo e fácil de ler.

Dicas de Teste

Quando você invocar a função, ela verificará os aniversários que coincidem com a data de hoje. Se houver uma correspondência, você receberá uma mensagem de texto com os detalhes. Para testar a função de forma eficaz:

  • Adicione um ou mais aniversários em seu banco de dados que correspondam à data atual.
  • Verifique o console ou os logs da CLI para confirmar que a função foi executada com sucesso.

🎁 Conclusão

Aqui está o que conseguimos hoje:

✅ Configuramos doctl e criamos um namespace para nosso projeto.
✅ Refatoramos o script em Python para implantação.
✅ Empacotamos e implantamos o Serviço de Lembrete de Aniversário nas Funções do DigitalOcean.
✅ Testamos a função na nuvem usando tanto a CLI quanto o Painel do DigitalOcean.

Próximo passo: Embora este seja um grande avanço, ainda estamos executando a função manualmente. No próximo post, vamos automatizar esse processo para que o Serviço de Lembrete de Aniversário seja executado automaticamente todos os dias a uma hora específica. Imagine acordar com um lembrete de texto sem levantar um dedo—vamos fazer isso acontecer amanhã! 🚀

Source:
https://www.digitalocean.com/community/tutorials/deploying-birthday-notifications-with-digitalocean-functions