Automated system for sending personalized mass emails using Python. Designed for email marketing campaigns, mass notifications, and business communications with sending rate control and detailed logging.
- Mass email sending from Excel files
- Template personalization using Jinja2
- Sending rate control to avoid spam blocks
- Sent emails logging in Excel
- Responsive HTML templates compatible with multiple email clients
- Dry-run mode for testing without sending real emails
- Detailed logging of all operations
- Clean architecture with domain and service separation
- Python 3.7+
- SMTP account (Gmail, Outlook, custom server, etc.)
Send-Mails/
├── src/
│ ├── config.py # Configuration and environment variables
│ ├── domain/
│ │ ├── email.py # Email data model
│ │ └── recipient.py # Recipient data model
│ ├── services/
│ │ ├── smtp_sender.py # SMTP sending service
│ │ ├── rate_limiter.py # Sending rate control
│ │ ├── html_renderer.py # Jinja2 template rendering
│ │ ├── excel_loader.py # Load recipients from Excel
│ │ └── sent_logger.py # Log sent emails
│ └── utils/
│ └── logger.py # Logging configuration
├── templates/
│ ├── invitation.html # Invitation template
│ └── reactivation.html # Reactivation template
├── data/
│ ├── recipients.xlsx # Recipients file
│ └── correos_enviados.xlsx # Sent emails log
├── main.py # Main script
├── generate_dummy_data.py # Test data generator
├── requirements.txt # Project dependencies
├── .env.example # Environment variables example
└── README.md # This file
git clone <repository-url>
cd Send-Mailspython -m venv venvWindows:
venv\Scripts\activateLinux/Mac:
source venv/bin/activatepip install -r requirements.txtCopy the .env.example file to .env and configure your SMTP credentials:
cp .env.example .envEdit .env with your credentials:
# SMTP Configuration
SMTP_SERVER=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your_email@gmail.com
SMTP_PASSWORD=your_password_or_app_password
FROM_EMAIL=your_email@gmail.com
# Sending limits (to avoid spam blocks)
MAX_EMAILS_PER_HOUR=100
MAX_EMAILS_PER_INTERVAL=10
INTERVAL_MINUTES=5If using Gmail, you need to generate an "App Password":
- Go to Google Account Settings
- Security > 2-Step Verification (must be enabled)
- App passwords
- Generate new password for "Mail"
- Use that password in
SMTP_PASSWORD
Create an Excel file at data/recipients.xlsx with the following columns:
| Name | Company | |
|---|---|---|
| example@email.com | John Doe | ABC Company |
| another@email.com | Jane Smith | XYZ Company |
Or generate test data:
python generate_dummy_data.pyNormal mode (sends real emails):
python main.pyTest mode (doesn't send emails, just simulates):
python main.py --dry-runHTML templates are in templates/. You can customize them using Jinja2 variables:
<p>Hello {{ name }},</p>
<p>Your company {{ company }} has been selected...</p>To change the template used, edit in main.py:73:
body_html = template_service.render("reactivation.html", context)To avoid being marked as spam, configure limits in .env:
MAX_EMAILS_PER_HOUR: Maximum emails per hour (default: 250)MAX_EMAILS_PER_INTERVAL: Maximum emails per interval (default: 10)INTERVAL_MINUTES: Interval duration in minutes (default: 5)
Example: 10 emails every 5 minutes = 120 emails/hour maximum
Edit in main.py:76:
email_content = EmailContent(
subject="Your custom subject here",
body_html=body_html,
to_email=recipient.email
)- Add columns to Excel
- Modify
src/services/excel_loader.pyif necessary - Update context in
main.py:69-72:
context = {
"name": recipient.name,
"company": recipient.company,
"new_variable": recipient.new_variable # Add here
}The system generates an app.log file with detailed information:
2026-01-26 10:00:00 - INFO - Starting email sender...
2026-01-26 10:00:01 - INFO - Loading recipients from data/recipients.xlsx...
2026-01-26 10:00:01 - INFO - Loaded 50 recipients.
2026-01-26 10:00:02 - INFO - Processing example@email.com...
2026-01-26 10:00:03 - INFO - Email sent successfully to example@email.com
Sent emails are logged in data/correos_enviados.xlsx with:
- Send date and time
- Company
- Recipient name
This allows:
- Sending audit
- Avoiding duplicates
- Campaign analysis
email.py: Defines email content modelrecipient.py: Defines recipient model
smtp_sender.py: Handles SMTP connection and sendingrate_limiter.py: Implements sending rate controlhtml_renderer.py: Renders Jinja2 templatesexcel_loader.py: Loads and validates data from Excelsent_logger.py: Logs sent emails to Excel
logger.py: Centralized logging configurationconfig.py: Configuration and environment variables management
- Verify that
SMTP_USERandSMTP_PASSWORDare correct - If using Gmail, make sure to use an "App Password"
- Verify that 2-Step Verification is enabled
- Verify that
SMTP_SERVERandSMTP_PORTare correct - Verify that your firewall allows outgoing connections on SMTP port
- Some ISPs block port 25, use 587 (TLS) or 465 (SSL)
- Configure SPF, DKIM, and DMARC on your domain
- Don't send too many emails in a short time
- Adjust sending limits in
.env - Use a verified domain instead of Gmail/Outlook
- Avoid spam words in subject and content
python generate_dummy_data.pyOr manually create the data/ folder and Excel file.
- Always use
--dry-runfirst to verify everything works - Keep backups of the recipients file
- Monitor the log file during mass sends
- Respect sending limits of your SMTP provider
- Personalize messages for better open rates
- Include unsubscribe option to comply with GDPR/CAN-SPAM
- Verify emails before sending (syntax, valid domains)
- Use responsive templates for mobile and desktop
- NEVER upload the
.envfile to the repository (it's in.gitignore) - Use app passwords instead of your main password
- Limit access to log file (may contain sensitive information)
- Consider encrypting the Excel file with recipients
Contributions are welcome. Please:
- Fork the project
- Create a branch for your feature (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is for internal use. All rights reserved.
- pandas: Excel data manipulation
- openpyxl: Excel file reading/writing
- jinja2: HTML template engine
- python-dotenv: Environment variables management
- Initial mass email sending system
- Support for customizable HTML templates
- Sending rate control
- Sent emails logging
- Dry-run mode for testing
- Invitation and reactivation templates
How many emails can I send per day? Depends on your SMTP provider:
- Gmail: ~500/day
- Outlook: ~300/day
- SendGrid/AWS SES: According to your plan
Can I use other SMTP providers?
Yes, just configure SMTP_SERVER, SMTP_PORT and credentials in .env
How do I add attachments?
Modify smtp_sender.py to include attachments using MIMEBase
Does it work with custom SMTP servers?
Yes, configure your server's SMTP parameters in .env
Can I send in plain text format?
Yes, modify EmailContent to include body_text and adjust smtp_sender.py
- Attachment support
- Web panel for campaign management
- Open and click statistics
- CRM integration
- Scheduled sending
- A/B testing of templates
- Email validation before sending
- Multi-language support
Sistema automatizado para el envío masivo de correos electrónicos personalizados usando Python. Diseñado para campañas de email marketing, notificaciones masivas y comunicaciones empresariales con control de tasas de envío y registro detallado.
- Envío masivo de correos desde archivos Excel
- Personalización de plantillas usando Jinja2
- Control de tasa de envío para evitar bloqueos por spam
- Registro de correos enviados en Excel
- Plantillas HTML responsivas compatibles con múltiples clientes de correo
- Modo dry-run para pruebas sin enviar correos reales
- Logging detallado de todas las operaciones
- Arquitectura limpia con separación de dominios y servicios
- Python 3.7+
- Cuenta SMTP (Gmail, Outlook, servidor personalizado, etc.)
Send-Mails/
├── src/
│ ├── config.py # Configuración y variables de entorno
│ ├── domain/
│ │ ├── email.py # Modelo de datos de Email
│ │ └── recipient.py # Modelo de datos de Destinatario
│ ├── services/
│ │ ├── smtp_sender.py # Servicio de envío SMTP
│ │ ├── rate_limiter.py # Control de tasa de envío
│ │ ├── html_renderer.py # Renderizado de plantillas Jinja2
│ │ ├── excel_loader.py # Carga de destinatarios desde Excel
│ │ └── sent_logger.py # Registro de correos enviados
│ └── utils/
│ └── logger.py # Configuración de logging
├── templates/
│ ├── invitation.html # Plantilla de invitación
│ └── reactivation.html # Plantilla de reactivación
├── data/
│ ├── recipients.xlsx # Archivo con destinatarios
│ └── correos_enviados.xlsx # Registro de envíos
├── main.py # Script principal
├── generate_dummy_data.py # Generador de datos de prueba
├── requirements.txt # Dependencias del proyecto
├── .env.example # Ejemplo de variables de entorno
└── README.md # Este archivo
git clone <url-del-repositorio>
cd Send-Mailspython -m venv venvWindows:
venv\Scripts\activateLinux/Mac:
source venv/bin/activatepip install -r requirements.txtCopiar el archivo .env.example a .env y configurar las credenciales SMTP:
cp .env.example .envEditar .env con tus credenciales:
# Configuración SMTP
SMTP_SERVER=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=tu_email@gmail.com
SMTP_PASSWORD=tu_password_o_app_password
FROM_EMAIL=tu_email@gmail.com
# Límites de envío (para evitar bloqueos por spam)
MAX_EMAILS_PER_HOUR=100
MAX_EMAILS_PER_INTERVAL=10
INTERVAL_MINUTES=5Si usas Gmail, necesitas generar una "Contraseña de aplicación":
- Ir a Configuración de cuenta de Google
- Seguridad > Verificación en dos pasos (debe estar activada)
- Contraseñas de aplicaciones
- Generar nueva contraseña para "Correo"
- Usar esa contraseña en
SMTP_PASSWORD
Crear un archivo Excel en data/recipients.xlsx con las siguientes columnas:
| Name | Company | |
|---|---|---|
| ejemplo@email.com | Juan Pérez | Empresa ABC |
| otro@email.com | María López | Empresa XYZ |
O generar datos de prueba:
python generate_dummy_data.pyModo normal (envía correos reales):
python main.pyModo de prueba (no envía correos, solo simula):
python main.py --dry-runLas plantillas HTML están en templates/. Puedes personalizarlas usando variables Jinja2:
<p>Hola {{ name }},</p>
<p>Tu empresa {{ company }} ha sido seleccionada...</p>Para cambiar la plantilla usada, editar en main.py:73:
body_html = template_service.render("reactivation.html", context)Para evitar ser marcado como spam, configura los límites en .env:
MAX_EMAILS_PER_HOUR: Máximo de correos por hora (default: 250)MAX_EMAILS_PER_INTERVAL: Máximo de correos por intervalo (default: 10)INTERVAL_MINUTES: Duración del intervalo en minutos (default: 5)
Ejemplo: 10 correos cada 5 minutos = 120 correos/hora máximo
Editar en main.py:76:
email_content = EmailContent(
subject="Tu asunto personalizado aquí",
body_html=body_html,
to_email=recipient.email
)- Agregar columnas al Excel
- Modificar
src/services/excel_loader.pysi es necesario - Actualizar el contexto en
main.py:69-72:
context = {
"name": recipient.name,
"company": recipient.company,
"nueva_variable": recipient.nueva_variable # Agregar aquí
}El sistema genera un archivo app.log con información detallada:
2026-01-26 10:00:00 - INFO - Starting email sender...
2026-01-26 10:00:01 - INFO - Loading recipients from data/recipients.xlsx...
2026-01-26 10:00:01 - INFO - Loaded 50 recipients.
2026-01-26 10:00:02 - INFO - Processing ejemplo@email.com...
2026-01-26 10:00:03 - INFO - Email sent successfully to ejemplo@email.com
Los correos enviados se registran en data/correos_enviados.xlsx con:
- Fecha y hora de envío
- Empresa
- Nombre del destinatario
Esto permite:
- Auditoría de envíos
- Evitar duplicados
- Análisis de campañas
email.py: Define el modelo de contenido de emailrecipient.py: Define el modelo de destinatario
smtp_sender.py: Maneja la conexión y envío SMTPrate_limiter.py: Implementa el control de tasa de envíohtml_renderer.py: Renderiza plantillas Jinja2excel_loader.py: Carga y valida datos desde Excelsent_logger.py: Registra correos enviados en Excel
logger.py: Configuración centralizada de loggingconfig.py: Gestión de configuración y variables de entorno
- Verifica que
SMTP_USERySMTP_PASSWORDsean correctos - Si usas Gmail, asegúrate de usar una "Contraseña de aplicación"
- Verifica que la verificación en dos pasos esté activada
- Verifica que
SMTP_SERVERySMTP_PORTsean correctos - Verifica que tu firewall permita conexiones salientes en el puerto SMTP
- Algunos ISP bloquean el puerto 25, usa 587 (TLS) o 465 (SSL)
- Configura SPF, DKIM y DMARC en tu dominio
- No envíes demasiados correos en poco tiempo
- Ajusta los límites de envío en
.env - Usa un dominio verificado en lugar de Gmail/Outlook
- Evita palabras spam en el asunto y contenido
python generate_dummy_data.pyO crea manualmente la carpeta data/ y el archivo Excel.
- Siempre usa
--dry-runprimero para verificar que todo funciona - Mantén backups del archivo de destinatarios
- Monitorea el archivo de log durante envíos masivos
- Respeta los límites de envío de tu proveedor SMTP
- Personaliza los mensajes para mejor tasa de apertura
- Incluye opción de desuscripción para cumplir con GDPR/CAN-SPAM
- Verifica los correos antes de enviar (sintaxis, dominios válidos)
- Usa plantillas responsivas para móviles y desktop
- NUNCA subas el archivo
.enval repositorio (está en.gitignore) - Usa contraseñas de aplicación en lugar de tu contraseña principal
- Limita el acceso al archivo de log (puede contener información sensible)
- Considera encriptar el archivo Excel con destinatarios
Las contribuciones son bienvenidas. Por favor:
- Fork del proyecto
- Crea una rama para tu feature (
git checkout -b feature/AmazingFeature) - Commit de tus cambios (
git commit -m 'Add some AmazingFeature') - Push a la rama (
git push origin feature/AmazingFeature) - Abre un Pull Request
Este proyecto es de uso interno. Todos los derechos reservados.
- pandas: Manipulación de datos Excel
- openpyxl: Lectura/escritura de archivos Excel
- jinja2: Motor de plantillas HTML
- python-dotenv: Gestión de variables de entorno
- Sistema inicial de envío masivo de correos
- Soporte para plantillas HTML personalizables
- Control de tasa de envío
- Registro de correos enviados
- Modo dry-run para pruebas
- Plantillas de invitación y reactivación
¿Cuántos correos puedo enviar por día? Depende de tu proveedor SMTP:
- Gmail: ~500/día
- Outlook: ~300/día
- SendGrid/AWS SES: Según tu plan
¿Puedo usar otros proveedores SMTP?
Sí, solo configura SMTP_SERVER, SMTP_PORT y credenciales en .env
¿Cómo agrego adjuntos?
Modifica smtp_sender.py para incluir adjuntos usando MIMEBase
¿Funciona con servidores SMTP personalizados?
Sí, configura los parámetros SMTP de tu servidor en .env
¿Puedo enviar en formato texto plano?
Sí, modifica EmailContent para incluir body_text y ajusta smtp_sender.py
- Soporte para adjuntos
- Panel web para gestión de campañas
- Estadísticas de apertura y clics
- Integración con CRM
- Programación de envíos
- A/B testing de plantillas
- Validación de emails antes de enviar
- Soporte para múltiples idiomas