Hace unos días tuve una tarea que consistía en enviar, mediante SMTP, unos correos a los clientes de mi cliente, algo así como un newsletter, pero al ser pocos correos y sin archivos adjuntos podíamos hacerlo con la configuración del SMTP, que este caso fue del servicio de email que ofrece GoDaddy. Aquí veremos cómo enviar correos son cURL y SMTP.
Como uno es curioso, puedo tardarme diez horas automatizando algo que haciéndolo a mano te llevaría una hora.
Contenido
Manos a la obra: comencé a diseñar el correo en Stripo y ya con el HTML listo, solo faltaba la parte divertida.
El proceso fue el siguiente:
- Tener el archivo separado por comas (CSV) de los correos de destino
- Afinar el HTML exportado de Stripo
- Obtener la configuración SMTP de GoDaddy
- Conectar con cURL al SMPTP y enviar los parámetros, headers necesarios y enviar el HTML
- Ejecutar el script
- Estar feliz con el resultado.
El último siempre es el más complicado.
Voy a documentar todos los errores que se me fueron presentando y la manera en que los resolví.
Error en cURL: Protocol not supported or disabled en libcurl
Todavía ni empezaba a enviar un simple correo con un “hello mundo” y ya tenía un error al ejecutar el comando básico 🤬🤬🤬:
curl --ssl-reqd --url "smtps://$SERVER:$PORT" \
--user "$USER:$PASS" \
--mail-from "$SENDER_ADDRESS" \
--mail-rcpt "$RECIPIENT_ADDRESS" \
--upload-file "$MAIL_CONTENT_FILE"
El error:
curl: (1) Protocol "smtps" not supported or disabled in libcurl
# o
curl: (1) Protocol "smtp" not supported or disabled in libcurl
Básicamente es porque la versión de cURL que nos bajamos de los repositorios como dnf
en mi caso que estoy en Fedora, está medio limitado en sus funcionalidades.
¿Cómo lo solucionamos el error curl: (1) Protocol “smtps” not supported or disabled in libcurl?
Primero bebemos bajarnos cURL con todas sus features, por decirlo de alguna manera.
wget https://curl.se/download/curl-8.2.1.tar.gz
Una vez descargado, lo vamos a compilar.
Nota: primero puedes intentar descargar las herramientas para poder compilar en caso de que no las tengas.
sudo dnf install make automake gcc gcc-c++ kernel-devel
Compilar cURL
Una vez instaladas, hacemos lo siguiente:
# Descargar cURL
wget https://curl.se/download/curl-8.2.1.tar.gz
# Extraemos los archivos y entramos al directorio
tar zxf curl-8.2.1.tar.gz
cd curl-8.2.1/
# Le damos el directorio en el que hará todo el proceso de
# configuración y compilación
./configure --prefix=/home/<TU_USUARIO>/curl_full
Otro error 🫣:
configure: error: select TLS backend(s) or disable TLS with --without-ssl.
Agregamos la opción para que nos compile con OpenSSL support, que debemos tenerlo instalado:
# Configure
/configure --prefix=/home/manuelhdez/curl_smtp --with-openssl
# Compilar
make
make install
Ahora si, una vez finalizado el proceso ejecutaremos /home/<TU_USUARIO>/curl_full/bin/curl --version
podemos ver que está funcionando.
En comparación con nuestra versión global instalada vemos que ahora los protocolos soportados incluyen a smtp
y smtps
.
Preparando todo para enviar correos son cURL y SMTP
Con cURL completo instalado ya podemos hacer un script en bash para enviar correos son cURL y SMTP.
Para más claridad, vamos a separar los scripts en dos archivos:
send.sh
: para solo enviar el correo y otro para procesar el archivo de los emails.process.sh
: para leer el archivo de emails y enviarle uno a uno asend.sh
.
Archivo de emails
Vamos a generar un ejemplo de emails. Se vería algo así:
fulano xxx,[email protected]
mengano yyy,[email protected]
perengano wwww,[email protected]
Script de envío
Nombremos al archivo send.sh
:
#!/usr/bin/env bash
SERVER='smtpout.secureserver.net'
PORT='465'
USER='<USUARIO_SMTP>'
PASS='<PASS_USUARIO_SMTP>'
SENDER_ADDRESS="[email protected]"
SENDER_NAME='<NOMBRE>'
RECIPIENT_NAME=$2
RECIPIENT_ADDRESS=$3
SUBJECT='Transportation Service in Mazatlan'
/home/<TU_USUARIO>/curl_full/bin/curl \
--no-progress-meter -vvv --ssl-reqd \
--url "smtps://$SERVER:$PORT" \
--user "$USER:$PASS" \
--mail-from "$SENDER_ADDRESS" \
--mail-rcpt "$RECIPIENT_ADDRESS" \
-H "Subject: $SUBJECT" \
-H "From: $SENDER_NAME <$SENDER_ADDRESS>" \
-H "To: $RECIPIENT_NAME <$RECIPIENT_ADDRESS>" \
--upload-file "$1"
Como vemos aquí hacemos uso de las siguientes variables (además de las obvias):
$1
: es la ruta y el nombre del template HTML que usaremos$2
: el nombre de la persona que recibe el correo$3
: el email de la persona que recibe el correo
Este script se puede usar de la siguiente manera:
# Damos permisos de ejecución
chmod +x send.sh
# Ejecutar
./send.sh <mi_archivo.html> "<nombre de la persona>" <correo@dominio>
Archivo HTML
Es casi seguro que hasta acá tengas un error entregando el correo por una falla en las cabeceras. Esto es lo que puedes obtener (yo lo obtuve de Gmail):
Reason: There was an error while attempting to deliver your message with [Subject: "??"] to [<[email protected]>](mailto:<[email protected]>). MTA p1plsmtpa20-12.prod.pzx3.secureserver.net received this response from the destination host IP - 142.251.2.26 - 550 , 550-5.7.1 [<UNA_IP>] Gmail has detected that this message is not RFC 5322
550-5.7.1 compliant:
550-5.7.1 'From' header is missing.
550-5.7.1 To reduce the amount of spam sent to Gmail, this message has been
550-5.7.1 blocked. For more information, go to
550-5.7.1 [https://support.google.com/mail/?p=RfcMessageNonCompliant](https://support.google.com/mail/?p=RfcMessageNonCompliant) and review
550 5.7.1 RFC 5322 specifications. p14-20020a056a000b4e12b006e6f6dd1735si2339065pfo.371 - gsmtp
¿Y ahora qué 🤔?
El error nos dice que que no tiene Subject
(“??”) y que no le hemos enviado el From
pero claramente lo estamos enviando en nuestro request de cURL. Si revisamos el comando para enviar el correo, vemos que usamos el switch -H
para enviarla.
Pues resulta que también nuestro archivo HTML debe tener indicados dentro del contenido esos datos. ¿Cómo lo hacemos? Debemos agregar esto al inicio del archivo
From: Mi Empresa <[email protected]>
To: Pepe Pecas <[email protected]>
Subject: Nuestro subject
Ahora intentamos enviarlo de nuevo y… 😮💨
NADA, que ahora tenemos otro error (espero que a ustedes no les pase):
552 Message contains bare LF and is violating 822.bis section 2.3 from subject
¡¡¡Jesucristo redentor!!!
Pues también resulta que el archivo HTML debe tener bien configurados los saltos de línea. Para no hacerla tan complicada, podemos ir a VS Code y cambiar esto:
Por default no estaría en CRLF
, sino en LF
, así que damos clic en LF
:
Cambiamos a CRLF
, guardamos y listo:
Una prueba más. El correo se ha enviado. 🤩🤩🤩
Vamos a nuestra bandeja y… Oh que la chin… 🥴🥴🥴
Me muestra el HTML no lo interpreta.
Aquí sí no le hemos indicado nada a nuestro comando, por lo que estaría enviando el archivo en texto plano, así que nuevamente vamos a nuestro archivo HTML y agregamos una línea justo abajo del Subject
:
Content-Type: text/html; charset="utf-8"
Guardamos (hay que asegurarnos que los saltos de línea siguen en CRLF
) y listo:
Por fin lo podemos ver en HTML.
BONUS: enviando los correos en batch con cURL y SMTP
Archivo HTML
Preparamos el archivo HTML index.html
, modificando la línea de To:
:
From: Mi Empresa <[email protected]>
To: CUSTOMER_NAME <CUSTOMER_EMAIL> <-------------------------ESTE
Subject: Servicio de transporte en Mazatlán
Content-Type: text/html; charset="utf-8"
CUSTOMER_NAME
y CUSTOMER_EMAIL
son los valores que vamos a reemplazar por los datos de nuestro cliente.
Script de proceso de correos
Ahora vamos crear otro script llamado process.sh
para leer el archivo de emails y enviarlo uno a uno:
#!/bin/bash
input=$1
counter=0
while IFS="," read -r name email
do
counter=$((counter+1))
file_to_use="index_tpl_$counter.html"
cp "index.html" $file_to_use
sed -i "s/CUSTOMER_NAME/$name/g" "$file_to_use"
sed -i "s/CUSTOMER_EMAIL/$email/g" "$file_to_use"
echo "Enviando a $name $email..."
./send.sh "$file_to_use" "$name" $email
sleep 5
done < "$input"
El proceso es el siguiente:
- Leemos el nombre del archivo y lo recogemos con
$1
en la variableinput
. - Hacemos un while leyendo el archivo
emails.txt
y asignamos el nombre de la persona enname
y el email en su respectiva variableemail
. - En cada iteración aumentamos nuestro
counter
- Asignamos un nombre de archivo en
file_to_use
. Este lo vamos a usar como plantilla. - Luego copiamos nuestro
index.html
con el nombre defile_to_use
. - Con el comando
sed
reemplazamos los dos valores en el template copiadoindex_X.html
:CUSTOMER_NAME
yCUSTOMER_EMAIL
. - Ejecutamos el comando enviando los valores a
send.sh
:- El template copiado y con los datos reemplazados
- Nombre del cliente
- Email del cliente
- Duermo 5 segundos para no darle tanta chamba al server.
La manera de llamarlo es el siguiente:
chmod +x process.sh
./process.sh emails.txt
Con esto ya podrás automatizar el envío de emails con cURL y SMTP. Hemos logrado ser felices 🥳 🥳 🥳.
Seguro se puede hacer sin crear archivo de copia, pero ando apurado y con ir borrando los archivos que creemos podemos evitarnos muchas copias del mismo archivo.
Gracias por leer.