Cómo acceder a Gmail usando PHP

Inicio / Cómo hacer para... / Cómo acceder a Gmail usando PHP

Este post está inspirado en un caso muy interesante que me tocó resolver para un cliente. Doy un poco de contexto para que se entienda de dónde viene el tema:

Una buena parte de los clientes de mi cliente llegan a través de correos que se reciben en info@...., claramente, te imaginarás que, dentro de los muchos que llegan, una parte es SPAM y la otra son contactos genuinos.

Separar la paja del trigo sería un desafío realmente interesante (Que probablemente involucraría algo de procesamiento del lenguaje natural, IA y esas cosas tan divertidas), pero… por el momento el presupuesto dio sólo para mejorar un poco el proceso de tratamiento del trigo una vez haya sido debidamente identificado.

Muy bien, entonces, el punto era que se estaba queriendo, además de re-enviar los correos útiles al equipo de ventas, subirlos de inmediato al sistema de newsletters de la empresa (Obviamente, se trataba de MailChimp).

Decidimos entonces crear una cuenta de GMail a la cual la persona encargada de identificar los correos de potenciales clientes pudiera re-enviarlos (además de al equipo de ventas) y, a partir de ahí, tener un robot que los procesara e incorporara la lista de mailing.

Técnicamente se trata de tres problemas:

  1. Cómo ingresar a GMail y descargar los correos
  2. Cómo procesar los datos descargados para extraer las direcciones de correo
  3. Cómo incorporar esos datos a MailChimp

Sobre el último de los problemas escribí acá, así que concentrémonos en los dos primeros:

Cómo ingresar a GMail usando PHP

Hay una primera parte que hace a la autorización de la aplicación (Muy parecido a lo que hicimos para entrar al Google Drive), luego está el tema de efectivamente ingresar al correo.

Lo más sencillo y directo es usar el SDK provisto por Google. En nuestro caso, nos interesan las clases Google_Client (Necesaria para acceder a cualquier servicio de Google) y Google_Service_Gmail (Específica para el procesamiento de peticiones a Gmail).

La creación de la instancia de Google_Client requiere de la existencia de un set de credenciales válidas (que se obtienen simplemente siguiendo un enlace que generará la propia aplicación).

Una vez obtenido este objeto lo usaremos para crear nuestra instancia de la segunda clase:

$service = new Google_Service_Gmail($client);

Y a partir de ahí usaremos la propiedad users_messages para traer los correos.

Cómo procesar correos descargados de GMail usando PHP

Una vez obtenido cada uno de los correos necesitaremos procesarlos.

Lo que sabemos es que los correos vienen codificados como texto MIME, con lo cual, necesitaremos algo de ayuda para procesarlos sin volvernos locos… por ejemplo, la ayuda de PHPMimeMailParser.

Claro que no todo es tan fácil… en particular, los mails en Gmail vienen además codificados usando base64 (Pero no el base64 que viene con PHP… es un base64 codificado para URLs… Nada muy terrible, sólo hay que saberlo para actuar en consecuencia: se necesita cambiar los caracteres - y _ por + y /respectivamente. Más detalles en https://medium.com/@jrdnrc/decoding-gmail-messages-in-php-408194aeb767).

Una vez hecho esto podremos extraer alegremente las diferentes partes del correo, por ejemplo:

$from = $parser->getHeader('from');

Para conocer el remitente. O:

$subject = $parser->getHeader('Subject')

Para el asunto.

Resultado final

Ahora sí, veamos el ejemplo completo.

Como usé Composer para instalar las librerías, lo primero que voy a mostrarte es el archivo composer.json:

{
    "name": "leeway/gmail2mailchimp",
    "require": {
        "google/apiclient": "^2.0",
        "php-mime-mail-parser/php-mime-mail-parser": "^2.9"
    },
    "authors": [
        {
            "name": "Mauro Chojrin",
            "email": "mauro.chojrin@leewayweb.com"
        }
    ]
}

Ahora sí, el script que da vida a todo esto import.php:

#!/usr/bin/php -q

<?php
require_once __DIR__ . '/vendor/autoload.php';

define('APPLICATION_NAME', 'Gmail2MailChimp');
define('CREDENTIALS_PATH', __DIR__.'/gmail2mailchimp.json');
define('CLIENT_SECRET_PATH', __DIR__ . '/client_secret.json');
define('SCOPES', implode(' ', array(
        Google_Service_Gmail::MAIL_GOOGLE_COM)
));

if (php_sapi_name() != 'cli') {
    throw new Exception('Esta aplicación sólo puede ejecutarse de la terminal.');
}

echo 'Usando credenciales de: '.CREDENTIALS_PATH.PHP_EOL;
/**
 * @return Google_Client
 */
function getClient()
{
    $client = new Google_Client();
    $client->setApplicationName(APPLICATION_NAME);
    $client->setScopes(SCOPES);
    $client->setAuthConfig(CLIENT_SECRET_PATH);
    $client->setAccessType('offline');

    // Load previously authorized credentials from a file.
    $credentialsPath = expandHomeDirectory(CREDENTIALS_PATH);
    if (file_exists($credentialsPath)) {
        $accessToken = json_decode(file_get_contents($credentialsPath), true);
    } else {
        // Request authorization from the user.
        $authUrl = $client->createAuthUrl();
        printf("Open the following link in your browser:\n%s\n", $authUrl);
        print 'Enter verification code: ';
        $authCode = trim(fgets(STDIN));

        // Exchange authorization code for an access token.
        $accessToken = $client->fetchAccessTokenWithAuthCode($authCode);

        // Store the credentials to disk.
        if (!file_exists(dirname($credentialsPath))) {
            mkdir(dirname($credentialsPath), 0700, true);
        }
        file_put_contents($credentialsPath, json_encode($accessToken));
        printf("Credentials saved to %s\n", $credentialsPath);
    }
    $client->setAccessToken($accessToken);

    // Refresh the token if it's expired.
    if ($client->isAccessTokenExpired()) {
        $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
        file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
    }
    return $client;
}

/**
 * Expands the home directory alias '~' to the full path.
 * @param string $path the path to expand.
 * @return string the expanded path.
 */
function expandHomeDirectory($path)
{
    $homeDirectory = getenv('HOME');
    if (empty($homeDirectory)) {
        $homeDirectory = getenv('HOMEDRIVE') . getenv('HOMEPATH');
    }
    return str_replace('~', realpath($homeDirectory), $path);
}


// Get the API client and construct the service object.
$client = getClient();
$service = new Google_Service_Gmail($client);

// Print the labels in the user's account.
$user = 'me';

$results = $service->users_messages->listUsersMessages($user, [ 'labelIds' => ['INBOX'] ]);

echo "Message count: " . count($results->getMessages()) . PHP_EOL;

$parser = new PhpMimeMailParser\Parser();

foreach ($results->getMessages() as $message) {
    if ($real_message = $service->users_messages->get(
        $user,
        $message->getId(),
        [
            'format' => 'raw',
        ]
    )) {
        $real_message = base64_decode(str_replace(['-', '_'], ['+', '/'], $real_message->getRaw()));
        $parser->setText($real_message);
        $from = $parser->getHeader('from');
        $body = $parser->getMessageBody();
        $matches = [];
        preg_match('/mailto:(.+)\]/', $body, $matches);
        if (count($matches) > 1) {
            $sender = $matches[1];
            echo 'Subject: "' . $parser->getHeader('Subject') . '" vino originalmente de: "' . $sender . '"' . PHP_EOL;
            echo "Subiendo a MailChimp" . PHP_EOL;
            if (syncMailchimp([
                    'email' => $sender,
                    'status' => 'subscribed',
                ])) {
                echo 'Exito!' . PHP_EOL;
                try {
                        $service->users_messages->delete(
                            'me',
                            $message->getId()
                        );
                        echo 'Eliminado del servidor!' . PHP_EOL;
                } catch (Exception $e) {
                        echo 'Error eliminando del servidor: ' . $e->getMessage() . PHP_EOL;
                }
            } else {
                echo 'Error :(' . PHP_EOL;
            }            
        }
    } else {
        echo 'No real message here...';
    }
}
echo 'Fin!' . PHP_EOL;

function syncMailchimp($data)
{
    $apiKey = 'XXXXXX';
    $listId = 'YYYYYY';

    $memberId = md5(strtolower($data['email']));
    $dataCenter = substr($apiKey, strpos($apiKey, '-') + 1);
    $url = 'https://' . $dataCenter . '.api.mailchimp.com/3.0/lists/' . $listId . '/members/' . $memberId;

    $json = json_encode([
        'email_address' => $data['email'],
        'status' => $data['status'], // "subscribed","unsubscribed","cleaned","pending"
	'source' => 'Cronjob',
    ]);

    $ch = curl_init($url);

    curl_setopt($ch, CURLOPT_USERPWD, 'user:' . $apiKey);
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);

    $result = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    return $httpCode;
}

Y por último, la línea del crontab:

0 17 * * 1-5 /usr/bin/php import.php

Para que corra automáticamente a las 17:00 de Lunes a Viernes.

Los archivos gmail2mailchimp.jsonclient_secret.json son los descargados de Google al configurar la aplicación.

Es un poco enrevesado, pero funciona :), no está mal, ¿cierto?

mchojrin

Director Académico y Docente at Leeway Academy
Hola! Soy Mauro Chojrin, estudié la Lic. en Ciencias de la Computación en la Universidad de Buenos Aires.

Me desempeño como docente de programación desde el año 1997.

Pasé por diferentes instituciones (Escuela Técnica ORT, Digital House, EducacionIT, ITMaster, Escuela DaVinci entre otros).

Actualmente coordino los cursos dictados en Leeway Academy y desarrollo sistemas usando PHP y framework Symfony

2 comentarios

Deja un comentario

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

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.