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

Bem-vindo ao Dia 4 dos 12 Dias de DigitalOcean! Ontem, adicionamos notificações 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, vamos levar 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 estiver conectado à internet—não há mais necessidade de executar o script manualmente na sua máquina. 🎉

Por que DigitalOcean Functions?

Às vezes, tudo o que você precisa é de um script simples que roda ocasionalmente. Gerenciar a infraestrutura para algo assim pode ser excessivo. É 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

Ao final de hoje, você saberá como:

  1. Configurar a doctl ferramenta CLI da DigitalOcean.
  2. Criar e conectar-se a um namespace sem servidor (a maneira 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 que você tem:

🧑‍🍳 Receita para o Dia 4: Implantando em Funcionalidades do DigitalOcean

Passo 1: Configure o CLI doctl

Se você já configurou o doctl em sua máquina, pode pular esta etapa. Para aqueles que precisam configurá-lo, siga 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. Nós a 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 da 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á fazê-lo 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ê ver 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 conectamos a um namespace. Vamos resolver 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-de-lembrete-de-aniversario" --region "nyc1"
    

  2. Conectar ao namespace:

    doctl serverless connect meu-lembrete-de-aniversario-namespace
    

  3. Verificar a conexão:

    doctl serverless status
    

Agora você deve 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 múltiplos projetos ou quiser verificar o namespace que você 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-lembrete-de-aniversário-serviço
    
  3. Renomeie as pastas para corresponder ao nosso caso de uso:

    mv pacotes/exemplo pacotes/lembretes
    mv pacotes/lembretes/olá pacotes/lembretes/aniversários
    mv pacotes/lembretes/aniversários/olá.py pacotes/lembretes/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 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 completar essas etapas, a estrutura do seu projeto deve parecer com isso:

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, pode executar o comando novamente ou renomeá-la manualmente no seu explorador de arquivos.

Passo 5: Atualizar Arquivos

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

1. Atualize project.yml

O arquivo project.yml é um arquivo de configuração que define a estrutura, variáveis de ambiente e funções do seu projeto serverless. 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 variáveis de ambiente para as Funções do DigitalOcean. Cada variável corresponde às credenciais necessárias para sua integração com o banco de dados e o 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 envio de 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 estejam empacotadas corretamente com sua função. O 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): DigitalOcean Functions espera 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 alinhar-se a esse requisito.

  3. Tudo o mais permanece o mesmo:
    A lógica de conexão com o banco de dados, as verificações de aniversários e a lógica de notificação por SMS não foram alteradas.

Passo 5: Empacotar e Implantar

Com tudo em ordem, implante seu projeto nas Funções do DigitalOcean:

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

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

  1. Acesse o Painel de Controle do DigitalOcean e navegue até Funções na barra lateral esquerda.
  2. Localize seu namespace (por exemplo, my-birthday-reminder-namespace).
  3. Verifique se sua função aparece sob o namespace, tipicamente listada como reminders/birthdays.
  4. Clique no nome da função para ver os detalhes, incluindo logs, configuração e histórico de invocações.

Passo 6: Teste Sua Função Implantada

Uma vez que sua função esteja implantada, é 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 o 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 do DigitalOcean

  1. Vá para 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. Veja a saída e os logs diretamente no console.

Esse 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 no seu banco de dados que correspondam à data atual.
  • Verifique o console ou os registros do CLI para confirmar que a função foi executada com sucesso.

🎁 Conclusão

Aqui está o que realizamos hoje:

✅ Configuramos o doctl e criamos um namespace para nosso projeto.
✅ Refatoramos o script 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 o 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 por mensagem de texto sem levantar um dedo—vamos fazer isso acontecer amanhã! 🚀

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