Comunicação programática é um pilar de qualquer sistema moderno — confirmações de cadastro, alertas de sistema, notificações de pagamento, relatórios automáticos, mensagens de suporte. Python tem suporte nativo a SMTP para e-mails e integração direta com as principais APIs de comunicação do mercado: SendGrid, Twilio, Slack e WhatsApp. Neste artigo veremos como implementar cada uma dessas integrações de forma profissional.
E-mail com smtplib: Protocolo Nativo
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email import encoders
from pathlib import Path
import os
class ClienteEmail:
"""Cliente SMTP reutilizável com suporte a TLS."""
def __init__(
self,
host: str,
porta: int,
usuario: str,
senha: str,
tls: bool = True
):
self.host = host
self.porta = porta
self.usuario = usuario
self.senha = senha
self.tls = tls
def _criar_conexao(self) -> smtplib.SMTP:
smtp = smtplib.SMTP(self.host, self.porta)
smtp.ehlo()
if self.tls:
smtp.starttls()
smtp.ehlo()
smtp.login(self.usuario, self.senha)
return smtp
def enviar(
self,
destinatarios: list,
assunto: str,
corpo_texto: str,
corpo_html: str = None,
anexos: list = None,
cc: list = None,
bcc: list = None,
remetente_nome: str = None
) -> bool:
msg = MIMEMultipart("alternative")
# Cabeçalhos
remetente = (
f"{remetente_nome} <{self.usuario}>"
if remetente_nome else self.usuario
)
msg["From"] = remetente
msg["To"] = ", ".join(destinatarios)
msg["Subject"] = assunto
if cc:
msg["Cc"] = ", ".join(cc)
# Corpo texto puro (fallback)
msg.attach(MIMEText(corpo_texto, "plain", "utf-8"))
# Corpo HTML (preferido pelos clientes de e-mail)
if corpo_html:
msg.attach(MIMEText(corpo_html, "html", "utf-8"))
# Anexos
if anexos:
for caminho_anexo in anexos:
arquivo = Path(caminho_anexo)
if not arquivo.exists():
continue
with open(arquivo, "rb") as f:
parte = MIMEBase("application", "octet-stream")
parte.set_payload(f.read())
encoders.encode_base64(parte)
parte.add_header(
"Content-Disposition",
f"attachment; filename={arquivo.name}"
)
msg.attach(parte)
# Todos os destinatários (To + CC + BCC)
todos = destinatarios + (cc or []) + (bcc or [])
try:
with self._criar_conexao() as smtp:
smtp.sendmail(self.usuario, todos, msg.as_string())
print(f"E-mail enviado para {len(todos)} destinatário(s).")
return True
except smtplib.SMTPException as e:
print(f"Erro ao enviar e-mail: {e}")
return False
# Configurações por provedor
def gmail(usuario, senha):
return ClienteEmail("smtp.gmail.com", 587, usuario, senha)
def outlook(usuario, senha):
return ClienteEmail("smtp.office365.com", 587, usuario, senha)
def yahoo(usuario, senha):
return ClienteEmail("smtp.mail.yahoo.com", 587, usuario, senha)
# Uso
cliente = gmail(
os.getenv("EMAIL_USER"),
os.getenv("EMAIL_PASS") # use App Password no Gmail
)
cliente.enviar(
destinatarios=["ana@email.com"],
assunto="Boletim Escolar — Março 2024",
corpo_texto="Segue em anexo seu boletim escolar.",
corpo_html="""
<h2>Olá, Ana!</h2>
<p>Segue em anexo seu <strong>boletim escolar</strong> de Março 2024.</p>
<p>Em caso de dúvidas, entre em contato com a secretaria.</p>
""",
anexos=["boletim_2024-03.pdf"],
remetente_nome="Sistema Escolar"
)
Templates de E-mail com Jinja2
from jinja2 import Environment, FileSystemLoader
from pathlib import Path
def criar_ambiente_templates():
Path("templates/email").mkdir(parents=True, exist_ok=True)
return Environment(
loader=FileSystemLoader("templates/email"),
autoescape=True
)
# templates/email/base.html
TEMPLATE_BASE = """
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<style>
body { font-family: Arial, sans-serif; background: #f4f4f4; margin: 0; }
.container { max-width: 600px; margin: 20px auto; background: white;
border-radius: 8px; overflow: hidden; }
.header { background: #1F4E79; color: white; padding: 30px;
text-align: center; }
.header h1 { margin: 0; font-size: 22px; }
.body { padding: 30px; color: #333; line-height: 1.6; }
.footer { background: #f4f4f4; padding: 15px; text-align: center;
font-size: 12px; color: #888; }
.btn { display: inline-block; background: #2E75B6; color: white;
padding: 12px 24px; border-radius: 4px; text-decoration: none;
font-weight: bold; margin: 15px 0; }
.alerta { background: #FCE4D6; border-left: 4px solid #C00000;
padding: 12px; border-radius: 4px; }
.sucesso { background: #E2EFDA; border-left: 4px solid #375623;
padding: 12px; border-radius: 4px; }
table { width: 100%; border-collapse: collapse; }
th { background: #2E75B6; color: white; padding: 10px; }
td { padding: 8px; border-bottom: 1px solid #eee; }
tr:nth-child(even) td { background: #f9f9f9; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>{% block titulo %}Sistema Escolar{% endblock %}</h1>
</div>
<div class="body">
{% block conteudo %}{% endblock %}
</div>
<div class="footer">
<p>© 2024 Sistema Escolar. Este é um e-mail automático.</p>
<p><a href="{{ url_cancelar }}">Cancelar inscrição</a></p>
</div>
</div>
</body>
</html>
"""
# templates/email/boas_vindas.html
TEMPLATE_BOAS_VINDAS = """
{% extends "base.html" %}
{% block titulo %}Bem-vindo(a), {{ nome }}!{% endblock %}
{% block conteudo %}
<p>Olá, <strong>{{ nome }}</strong>!</p>
<p>Sua conta foi criada com sucesso no <strong>Sistema Escolar</strong>.</p>
<div class="sucesso">
<strong>Seus dados de acesso:</strong><br>
E-mail: {{ email }}<br>
Senha temporária: {{ senha_temp }}
</div>
<p>Por segurança, recomendamos alterar sua senha no primeiro acesso.</p>
<a href="{{ url_acesso }}" class="btn">Acessar o Sistema</a>
<p>Em caso de problemas, entre em contato com a secretaria.</p>
{% endblock %}
"""
class ServicoEmail:
def __init__(self, cliente: ClienteEmail):
self.cliente = cliente
self.env = criar_ambiente_templates()
# Salva templates em disco
Path("templates/email/base.html").write_text(TEMPLATE_BASE)
Path("templates/email/boas_vindas.html").write_text(TEMPLATE_BOAS_VINDAS)
def _renderizar(self, template_nome: str, contexto: dict) -> str:
template = self.env.get_template(template_nome)
return template.render(**contexto, url_cancelar="#")
def boas_vindas(self, nome: str, email: str, senha_temp: str):
html = self._renderizar("boas_vindas.html", {
"nome": nome,
"email": email,
"senha_temp": senha_temp,
"url_acesso": "https://sistema.escolar.com/login"
})
texto = f"Olá, {nome}! Sua conta foi criada. Senha temporária: {senha_temp}"
return self.cliente.enviar(
destinatarios=[email],
assunto=f"Bem-vindo(a) ao Sistema Escolar, {nome}!",
corpo_texto=texto,
corpo_html=html,
remetente_nome="Sistema Escolar"
)
def boletim(self, aluno: dict, pdf_path: str):
html = f"""
<h2>Olá, {aluno['nome']}!</h2>
<p>Seu boletim do mês está disponível em anexo.</p>
<table>
<tr><th>Disciplina</th><th>Nota</th><th>Status</th></tr>
{''.join(
f"<tr><td>{d['nome']}</td><td>{d['nota']}</td>"
f"<td>{'✓' if d['nota'] >= 6 else '✗'}</td></tr>"
for d in aluno.get('disciplinas', [])
)}
</table>
"""
return self.cliente.enviar(
destinatarios=[aluno["email"]],
assunto="Seu Boletim Escolar está disponível",
corpo_texto=f"Olá {aluno['nome']}, seu boletim está em anexo.",
corpo_html=html,
anexos=[pdf_path]
)
SendGrid: API Profissional de E-mail
pip install sendgrid
import os
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import (
Mail, To, From, Subject,
PlainTextContent, HtmlContent,
Attachment, FileContent, FileName,
FileType, Disposition
)
import base64
class ClienteSendGrid:
def __init__(self, api_key: str, remetente: str, nome_remetente: str = ""):
self.sg = SendGridAPIClient(api_key)
self.remetente = remetente
self.nome = nome_remetente
def enviar(
self,
destinatario: str,
assunto: str,
html: str,
texto: str = None,
anexos: list = None
) -> bool:
mensagem = Mail(
from_email=From(self.remetente, self.nome),
to_emails=To(destinatario),
subject=Subject(assunto),
plain_text_content=PlainTextContent(texto or ""),
html_content=HtmlContent(html)
)
if anexos:
for caminho in anexos:
arquivo = Path(caminho)
with open(arquivo, "rb") as f:
dados = base64.b64encode(f.read()).decode()
anexo = Attachment(
file_content=FileContent(dados),
file_name=FileName(arquivo.name),
file_type=FileType("application/octet-stream"),
disposition=Disposition("attachment")
)
mensagem.attachment = anexo
try:
resposta = self.sg.send(mensagem)
print(f"SendGrid: {resposta.status_code}")
return resposta.status_code in [200, 202]
except Exception as e:
print(f"Erro SendGrid: {e}")
return False
sg = ClienteSendGrid(
api_key=os.getenv("SENDGRID_API_KEY"),
remetente="noreply@escola.com",
nome_remetente="Sistema Escolar"
)
sg.enviar(
destinatario="aluno@email.com",
assunto="Confirmação de Matrícula",
html="<h1>Matrícula confirmada!</h1><p>Bem-vindo(a).</p>",
texto="Matrícula confirmada! Bem-vindo(a)."
)
Twilio: SMS e WhatsApp
pip install twilio
import os
from twilio.rest import Client
from twilio.base.exceptions import TwilioRestException
class ClienteTwilio:
def __init__(self, account_sid: str, auth_token: str, numero: str):
self.client = Client(account_sid, auth_token)
self.numero = numero # número Twilio: +15551234567
def enviar_sms(self, destinatario: str, mensagem: str) -> bool:
"""Envia SMS. destinatario no formato +5511999999999"""
try:
msg = self.client.messages.create(
body=mensagem,
from_=self.numero,
to=destinatario
)
print(f"SMS enviado: {msg.sid}")
return True
except TwilioRestException as e:
print(f"Erro Twilio: {e}")
return False
def enviar_whatsapp(self, destinatario: str, mensagem: str) -> bool:
"""
Envia WhatsApp via Twilio Sandbox.
destinatario: whatsapp:+5511999999999
"""
try:
msg = self.client.messages.create(
body=mensagem,
from_=f"whatsapp:{self.numero}",
to=f"whatsapp:{destinatario}"
)
print(f"WhatsApp enviado: {msg.sid}")
return True
except TwilioRestException as e:
print(f"Erro WhatsApp: {e}")
return False
def enviar_whatsapp_template(
self,
destinatario: str,
template_sid: str,
variaveis: dict
) -> bool:
"""Envia mensagem usando template aprovado pelo WhatsApp."""
try:
msg = self.client.messages.create(
from_=f"whatsapp:{self.numero}",
to=f"whatsapp:{destinatario}",
content_sid=template_sid,
content_variables=str(variaveis)
)
return True
except TwilioRestException as e:
print(f"Erro template: {e}")
return False
twilio = ClienteTwilio(
account_sid=os.getenv("TWILIO_ACCOUNT_SID"),
auth_token= os.getenv("TWILIO_AUTH_TOKEN"),
numero= os.getenv("TWILIO_NUMBER")
)
# SMS
twilio.enviar_sms(
"+5511999999999",
"Sua nota foi lançada: 8.5. Acesse o portal para ver o boletim."
)
# WhatsApp
twilio.enviar_whatsapp(
"+5511999999999",
"🎓 *Sistema Escolar*\nOlá, Ana! Sua nota de Matemática foi lançada: *8.5*"
)
Slack: Notificações para Equipes
pip install slack-sdk
import os
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
class ClienteSlack:
def __init__(self, token: str):
self.client = WebClient(token=token)
def enviar_mensagem(self, canal: str, texto: str) -> bool:
try:
self.client.chat_postMessage(channel=canal, text=texto)
return True
except SlackApiError as e:
print(f"Erro Slack: {e.response['error']}")
return False
def enviar_bloco(self, canal: str, blocos: list) -> bool:
"""Envia mensagem com Block Kit — layout rico."""
try:
self.client.chat_postMessage(channel=canal, blocks=blocos)
return True
except SlackApiError as e:
print(f"Erro Slack: {e.response['error']}")
return False
def notificar_alerta(self, canal: str, titulo: str, mensagem: str, urgente: bool = False):
cor = "#C00000" if urgente else "#2E75B6"
emoji = "🚨" if urgente else "ℹ️"
blocos = [
{
"type": "header",
"text": {"type": "plain_text", "text": f"{emoji} {titulo}"}
},
{
"type": "section",
"text": {"type": "mrkdwn", "text": mensagem}
},
{
"type": "context",
"elements": [{
"type": "mrkdwn",
"text": f"*Sistema Escolar* | {datetime.now().strftime('%d/%m/%Y %H:%M')}"
}]
}
]
return self.enviar_bloco(canal, blocos)
def notificar_boletim(self, canal: str, aluno: str, nota: float):
status = "✅ Aprovado" if nota >= 6 else "❌ Reprovado"
blocos = [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": f"*Nova nota lançada*\n*Aluno:* {aluno}\n*Nota:* {nota:.1f}\n*Status:* {status}"
}
},
{"type": "divider"},
{
"type": "actions",
"elements": [{
"type": "button",
"text": {"type": "plain_text", "text": "Ver Boletim"},
"url": "https://sistema.escolar.com/boletim"
}]
}
]
return self.enviar_bloco(canal, blocos)
from datetime import datetime
slack = ClienteSlack(token=os.getenv("SLACK_BOT_TOKEN"))
slack.notificar_alerta(
canal="#alertas-sistema",
titulo="Erro no processamento",
mensagem="Falha ao gerar boletins da turma B. Verificar logs.",
urgente=True
)
slack.notificar_boletim(
canal="#notas",
aluno="Ana Silva",
nota=9.5
)
Exemplo Completo: Sistema Centralizado de Notificações
from enum import Enum
from dataclasses import dataclass
from typing import Optional
import os
class Canal(str, Enum):
EMAIL = "email"
SMS = "sms"
WHATSAPP = "whatsapp"
SLACK = "slack"
@dataclass
class Notificacao:
destinatario: str
assunto: str
mensagem: str
mensagem_html: Optional[str] = None
canal: Canal = Canal.EMAIL
urgente: bool = False
anexos: list = None
class SistemaNotificacoes:
def __init__(self):
self._email = ClienteEmail(
host=os.getenv("SMTP_HOST", "smtp.gmail.com"),
porta=587,
usuario=os.getenv("EMAIL_USER"),
senha=os.getenv("EMAIL_PASS")
)
self._twilio = ClienteTwilio(
os.getenv("TWILIO_ACCOUNT_SID"),
os.getenv("TWILIO_AUTH_TOKEN"),
os.getenv("TWILIO_NUMBER")
)
self._slack = ClienteSlack(os.getenv("SLACK_BOT_TOKEN"))
def enviar(self, notif: Notificacao) -> bool:
print(f"[{notif.canal.value.upper()}] → {notif.destinatario}: {notif.assunto}")
if notif.canal == Canal.EMAIL:
return self._email.enviar(
destinatarios=[notif.destinatario],
assunto=notif.assunto,
corpo_texto=notif.mensagem,
corpo_html=notif.mensagem_html,
anexos=notif.anexos or []
)
elif notif.canal == Canal.SMS:
return self._twilio.enviar_sms(notif.destinatario, notif.mensagem)
elif notif.canal == Canal.WHATSAPP:
return self._twilio.enviar_whatsapp(notif.destinatario, notif.mensagem)
elif notif.canal == Canal.SLACK:
return self._slack.notificar_alerta(
canal=notif.destinatario,
titulo=notif.assunto,
mensagem=notif.mensagem,
urgente=notif.urgente
)
return False
def enviar_multiplos(self, notificacoes: list) -> dict:
resultados = {"sucesso": 0, "falha": 0}
for notif in notificacoes:
if self.enviar(notif):
resultados["sucesso"] += 1
else:
resultados["falha"] += 1
return resultados
# Uso
sistema = SistemaNotificacoes()
# Notificar resultado de prova por múltiplos canais
aluno = {
"nome": "Ana Silva",
"email": "ana@email.com",
"telefone": "+5511999999999",
"nota": 9.5
}
notificacoes = [
Notificacao(
destinatario=aluno["email"],
assunto="Sua nota foi lançada!",
mensagem=f"Olá {aluno['nome']}, sua nota é {aluno['nota']}.",
mensagem_html=f"<h2>Olá, {aluno['nome']}!</h2><p>Nota: <strong>{aluno['nota']}</strong></p>",
canal=Canal.EMAIL
),
Notificacao(
destinatario=aluno["telefone"],
assunto="Nota lançada",
mensagem=f"Olá {aluno['nome']}! Sua nota: {aluno['nota']}. Aprovado(a)! 🎉",
canal=Canal.SMS
),
Notificacao(
destinatario="#professores",
assunto="Nota lançada",
mensagem=f"*{aluno['nome']}* recebeu nota *{aluno['nota']}*",
canal=Canal.SLACK
),
]
resultado = sistema.enviar_multiplos(notificacoes)
print(f"\nResultado: {resultado['sucesso']} enviados, {resultado['falha']} falhas")
Resumo
smtplibenvia e-mails via SMTP com suporte a TLS, anexos, HTML e múltiplos destinatários- Templates Jinja2 separam conteúdo de código — essencial para e-mails com layout profissional
- SendGrid é a escolha para envios em escala — alta entregabilidade e métricas detalhadas
- Twilio integra SMS e WhatsApp com uma única API — ideal para notificações críticas
- Slack SDK permite notificações ricas com Block Kit — botões, seções e contexto formatado
- Um sistema centralizado de notificações abstrai os canais — adicionar novos não quebra o código existente
- Sempre use variáveis de ambiente para credenciais de APIs — nunca coloque tokens no código
Referências e Leituras Complementares
- smtplib — documentação oficial — https://docs.python.org/3/library/smtplib.html
- email.mime — documentação oficial — https://docs.python.org/3/library/email.mime.html
- SendGrid Python SDK — https://github.com/sendgrid/sendgrid-python
- Twilio Python SDK — https://www.twilio.com/docs/libraries/python
- Slack SDK para Python — https://slack.dev/python-slack-sdk/
- Slack Block Kit Builder — https://app.slack.com/block-kit-builder
- SWEIGART, Al. Automate the Boring Stuff with Python. 2. ed. No Starch Press, 2019. Cap. 16 — envio de e-mails e SMS com Python.
- PERCIVAL, Harry; GREGORY, Bob. Architecture Patterns with Python. O'Reilly Media, 2020. Cap. 8 — adaptadores para serviços externos e testes de integração.