Symfony: cómo evitar ser SPAM controlando los bounces y complaints de tus emails

2020-02-10 - Categorías: GNU/Linux / PHP / Symfony
Symfony, evitando ser marcado como SPAMMER controlando los bounces y complaints de los emails..

Hoy traigo un codekata bien cargado, sobre Symfony y los emails. Este post, junto con el anterior sobre cómo enviar muchos emails.. te dará el poder de enviar infinitos emails gratis. Pero un gran poder conlleva una gran responsabilidad 😀 o serás marcado como el supervillano de los emails, el SPAMMER 😱

Fuera de bromas, este es un post de pruebas, un codekata usando la última versión 5 de Symfony, con Symfony Flex, y Swiftmailer para el envío de los emails. Con este codekata podemos controlar que los emails que enviamos, lleguen a destino correctamente, para que no nos marquen como SPAMMERS. Evitamos así que nuestro servidor entre en listas negras de bloqueo. He elegido Symfony, el framework PHP de referencia para grandes proyectos web. También Swiftmailer ya que quería invertir poco tiempo, y viene muy bien integrado con Symfony 😜 La misma teoría y lógica también serviría para cualquier otro lenguaje como Java o Python.

Recapitulando, sobre el envío de muchos emails.. si queremos hacer las cosas bien, no debemos de enviar emails a personas que no quieren recibir dichos emails. Tampoco debemos enviar emails a buzones de correo que no existen. Y tampoco debemos de enviar emails con contenido infectado, esto es evidente, pero no está de más dejarlo claro para evitar malos entendidos 🙄 Continuando con el post anterior sobre cómo enviar muchos, muchos, muchos emails gratis..

Escenario, el peligro de acabar con el servidor de correo en lista negra, por SPAMMER

Nuestro punto de partida es que tenemos un servidor de correo electrónico normal y corriente. Lo siguiente que queremos es enviar muchos emails, sin pagar por un servidor de correo de alta disponibilidad. Ya tenemos todo instalado y funcionando, podemos enviar muchos emails poco a poco con una cola de emails. Queremos ahorrarnos los cientos de € que puede suponer el enviar muchos mailings masivos al año 🤑

Debemos de mantener una buena reputación de nuestro servidor.

El usar nuestro propio servidor SMTP, nos quita los límites. Pero podremos meter la pata hasta el fondo, enviando demasiados emails sin control, y acabaremos con la IP y dominio marcados como SPAMMERS en las listas negras mundiales. Mantener una buena reputación de nuestro servidor es muy importante, es la única forma. Resumiendo, si enviamos emails no deseados es malo.

La solución, controlar los bounces y complaints

La solución es bien simple, debemos de evitar enviar emails que no debemos de enviar. Me explico a continuación. Se reciben respuestas con cada email que llega mal que enviamos, de forma que sabremos si:

  • El email ha llegado correctamente, pero no se ha podido entregar por problemas en el servidor de destino (SOFT BOUNCES).
  • El email ha llegado correctamente, pero el destinatario lo ha marcado como SPAM (COMPLAINTS).
  • El email del destinatario ya no existe (HARD BOUNCES).
  • Y no recibimos respuesta de error si todo ha ido bien.. 😂

Simplemente tendremos que mantener una lista de emails, marcando los emails con sumo cuidado para no enviarles si no debemos de hacerlo. De lo contrario, nuestro servidor y dominio quedarán marcados en las listas negras, y bloqueados por muuuuucho tiempo. Luego es muy complicado sacar un servidor de las listas negras, aunque tampoco es imposible. Pero esto último de nuevo, es otra historia..

Al grano, creando un proyecto desastre en Symfony para automatizar el proceso

Como hace un tiempo que no actualizamos Composer, ni el nuevo comando de Symfony, ejecutamos lo siguiente para hacerlo. Doy por hecho que estamos en GNU/Linux, o en el servidor lo tenemos por lo menos:

sudo composer self-update -v
sudo symfony -v

..y seguimos las instrucciones. Si todavía no los tenemos instalados, tenemos que seguir las siguientes instrucciones de la documentación oficial para ello:

A fecha de hoy también podemos instalar la última versión disponible en PHP, la 7.4, con el siguiente comando:

sudo apt install php7.4-curl php7.4-fpm php7.4-gd php7.4-imap php7.4-intl php7.4-json php7.4-mbstring php7.4-mysql php7.4-opcache php7.4-soap php7.4-xml php7.4-xmlrpc php7.4-zip

..ya tenemos toda la parte del sistema actualizado. Lo siguiente será crear el proyecto, desde línea de comandos ejecutamos algo como lo siguiente:

symfony new prueba

..con lo que debemos ver algo como lo siguiente:

Symfony Flex, creando un proyecto nuevo en Symfony 5..

Ahora toca instalar los componentes que vamos a querer en este proyecto desastre:

cd prueba
composer require --dev maker
composer require swiftmailer

Esperamos un poco, y si todo ha ido bien, ya tenemos todos los componentes necesarios en el proyecto para las pruebas siguientes.

Creando un email para enviar y otro para manejar las respuestas

Lo siguiente es tener 2 emails disponibles para usar:

  • Uno para enviar los emails, por ejempo: aplicacion@tudominio.com
  • Otro para recibir las respuestas de los bounces y complaints: bounces.complaints@tudominio.com

Lo siguiente será configurar el de envío en el fichero .env así:

MAILER_URL=smtp://tudominio.com:587?encryption=tls&auth_mode=login&username=aplicacion@tudominio.com&password=tucontraseña

En este paso tendrás que coger los parámetros de configuración que hayas usado el email para enviar.

Comando Symfony para enviar unos emails de pruebas

Lo siguiente es hacer varios envíos. Lo más sencillo puede ser crear un comando de consola que envíe unos emails, y veremos entonces las respuestas de fallo. Desde línea de comandos podemos hacer:

Symfony, creando comando para enviar unos emails..

Un código fuente de comando, que envía 1 email, podría ser el siguiente:

<?php

namespace App\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

class SendSomeEmailsCommand extends Command
{
    protected static $defaultName = 'app:send-some-emails';
    private $mailer;

    public function __construct(
        \Swift_Mailer $mailer)
    {
        $this->mailer = $mailer;

        parent::__construct();
    }

    protected function configure()
    {
        $this
            ->setDescription('Sending some emails..')
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);

        $message = (new \Swift_Message())
            ->setFrom('aplicacion@tudominio.com')
            ->setTo('emailquenoexisteeee@gmail.com')
            ->setSubject('Esto es una prueba')
            ->setBody(
                '<p>Esto es una prueba de envío..</p>',
                'text/html'
            )
            ->addPart(
                'Esto es una prueba de envío..',
                'text/plain'
            )
            ->setReturnPath('bounces.complaints@tudominio.com')
        ;

        $mailResponse = $this->mailer->send($message, $failures);
        //var_dump($mailResponse);
        //var_dump($failures);

        $io->success('Some emails sent!');

        return 0;
    }
}

Éste código simplemente envía un email a una dirección de Gmail que no debería de existir, para así forzar un SOFT BOUNCE. Muy importante no olvidar la dirección de respuesta de los bounces y complaints. Esto se hace con el código de arriba, en la siguiente:

->setReturnPath('bounces.complaints@tudominio.com')

..así le estamos diciendo al servidor, que si hay algún problema, envíe la respuesta a bounces.complaints@tudominio.com. De esta forma, podremos poner en consola:

php bin/console app:send-some-emails

..y se enviará el email de prueba. ¿A que es sencillo?

Visualizando las respuestas para ver si hay bounces o complaints

Lo siguiente será ver la respuesta de error de que no existe el destinatario, procesarlo en nuestra aplicación, para no volver a enviar a dicho destino, en este caso.. Podemos ir de nuevo a la línea de comandos y ejecutamos:

Lo siguiente es un código fuente que procesa las respuestas. Recorre el buzón de respuestas en PHP con IMAP puede ser el siguiente, también en un comando de consolta Symfony:

<?php

namespace App\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

class ProcessBouncesAndComplaintsCommand extends Command
{
    protected static $defaultName = 'app:process-bounces-and-complaints';

    protected function configure()
    {
        $this
            ->setDescription('Receive bounces and complaints and make actions.')
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);

        $hostname = '{tudominio.com:143}INBOX';
        $username = 'bounces.complaints@tudominio.com';
        $password = 'tucontraseña?';

        $inbox = imap_open($hostname, $username, $password);
        $emails = imap_search($inbox, 'ALL');
        if ($emails) {
            foreach ($emails as $email_number) {
                echo 'An email>>>>'.PHP_EOL;

                //$overview = imap_fetch_overview($inbox, $email_number, 0);
                //var_dump($overview);

                //$message = imap_fetchbody($inbox, $email_number, 1.2);
                //var_dump($message);

                $rawFullMessage = imap_fetchbody($inbox, $email_number, '');

                echo $rawFullMessage.PHP_EOL;
                echo '<<<<-------------------------------------------------------'.PHP_EOL;
                echo PHP_EOL;
            }
        }
        echo 'All emails listed!'.PHP_EOL;

        echo 'Close the connection.. ';
        imap_close($inbox);
        echo 'done!'.PHP_EOL;

        $io->success('Bounces and complaints processed!');

        return 0;
    }
}

Como resultado de ejecutar este último comando tenemos que ver algo parecido a esto si todo ha ido bien:

Symfony, comando procesando los bounces y complaints en respuestas que hay en un buzón email..

Si nos fijamos con lupa con este BOUNCE, podemos ver cosas como lo siguiente:

Diagnostic-Code: smtp; 550-5.1.1 The email account that you tried to reach does
    not exist. Please try 550-5.1.1 double-checking the recipient's email
    address for typos or 550-5.1.1 unnecessary spaces. Learn more at 550 5.1.1
    https://support.google.com/mail/?p=NoSuchUser o5si4948355wrm.273 - gsmtp

Buscando un poco por aquí y por allá, seguro que podemos construir rápido una buena alternativa económica a contratar un servidor de emails de alta disponibilidad.

Terminando

¿Sencillo verdad? No tiene tanta historia el controlar los emails que enviamos. Supone un poco de esfuerzo, pero a la larga lo agradecerá nuestro bolsillo si seguimos desarrollando el codekata.

Sólo me queda referenciar a los estándares RFC. Espero no equivocarme, he encontrado un par de ellos relacionados, en el primero se definen los códigos de respuesta que deberían de llegar al email puesto en el Return-Path:

Otro día más.. ¡Un saludo!

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

 

© 2020 JnjSite.com - MIT license

Sitio hecho con WordPress, diseño y programación del tema por Jnj.