Статьи

Обработка входящей электронной почты с SendGrid

Если вы использовали службы блогов, такие как Tumblr или ныне не существующий Posterous, вы можете знать, что многие из них позволяют вам обходить свой веб-интерфейс и создавать сообщения по электронной почте, отправляя сообщение на определенный адрес электронной почты.

В этой статье я расскажу о том, как можно реализовать функцию отправки по электронной почте с помощью SendGrid. SendGrid — это сервис для отправки электронной почты — обычно массовый, но он также предлагает менее хорошо разрекламированную функцию для обработки входящей электронной почты. SendGrid может быть сконфигурирован для обработки всех входящих сообщений для данного домена, пингуя URI по вашему выбору, и, реализовав простой веб-крючок, вы можете соответствующим образом воздействовать на входящую почту.

Начиная

Я основал пример кода для этой статьи на этом скелетном приложении Slim Framework .

Чтобы упростить отладку приложения, мы можем реализовать ведение журнала на основе файлов, добавив следующее в раздел requirecomposer.json

 "slim/extras": "dev-develop",

Обновите экземпляр платформы в include/services.php

 $app = new Slim(array(
    'view' => new Twig(),
    'templates.path' => $c['config']['path.templates'],
    'log.writer' => new \Slim\Extras\Log\DateTimeFileWriter(array(
        'path' => dirname($c['config']['path.logs']),
        'name_format' => 'Y-m-d',
        'message_format' => '%label% - %date% - %message%'
    ))
));

Затем скопируйте пример файла конфигурации в config/config.php Кроме того, добавьте следующие строки, чтобы указать каталоги, в которых будут храниться файлы журналов и загруженные изображения:

 'path.logs'    => $basedir . 'logs/',
'path.uploads' => $basedir . 'public/uploads/'

Создайте каталоги и убедитесь, что они доступны для записи на веб-сервере.

Наше приложение предоставит зарегистрированным пользователям псевдоним электронной почты. Сопоставляя часть перед @ В реальном мире вам, вероятно, захочется сделать псевдоним более сложным для угадывания и, возможно, ограничить доступ к электронным письмам, отправленным с определенного адреса (хотя, конечно, это довольно просто подделать!).

Схема базы данных определяет две таблицы для хранения пользователей и сообщений соответственно:

 CREATE TABLE users (
    id INTEGER NOT NULL AUTO_INCREMENT,
    name VARCHAR(128) NOT NULL ,
    alias VARCHAR(45) NOT NULL ,

    PRIMARY KEY (id) ,
   INDEX alias (alias ASC)
);

CREATE  TABLE posts (
    id INTEGER NOT NULL AUTO_INCREMENT,
    title VARCHAR(255) NOT NULL,
    body TEXT NOT NULL,
    image varchar(255),
    user_id INTEGER NOT NULL,

    PRIMARY KEY (id)
);

Вам также понадобится учетная запись в SendGrid (бесплатный план должен быть более чем достаточным для этого примера приложения). После того, как вы зарегистрировались, перейдите на страницу для разработчиков, используя верхнюю панель навигации, и нажмите «Разбор входящих электронных писем» с правой стороны. Введите подходящие значения для вашего имени хоста и обратного вызова.

sendgrid-01

Когда электронное письмо получено для данного домена, SendGrid отправит запрос POST на ваш URL-адрес, который включает такую ​​информацию, как отправитель, получатель и текст, а также любые вложения файлов.

И, наконец, вам нужно добавить запись MX для домена, который вы используете. То, как вы это делаете, зависит от вашего хостинг-провайдера и может сильно различаться, поэтому обратитесь к соответствующей документации. Запись должна указывать на mx.sendgrid.net .

Построение обратного вызова

Приложение должно отвечать на запросы POST по указанному вами URL-адресу, например:

 $app->post('/endpoints/email', function () use ($app, $c) {

Если SendGrid попытается «пропинговать» вашу конечную точку и получит ответ 4xx или 5xx, он будет ставить в очередь и повторять запрос, и продолжит делать это в течение 3 дней. Поэтому важно, чтобы успешный пинг возвращал статус 200.

Когда SendGrid достигает конечной точки, он делает это с различной информацией из полученного е-мейла. Они описаны в онлайн-справочнике по API , но особенно нас интересуют:

sendgrid-02

В идеальном мире мы могли бы просто прочитать адрес электронной почты получателя непосредственно из поля «to», но это поле могло бы соответствовать любому из следующего неисчерпывающего списка примеров в зависимости от того, как было отправлено:

Нам нужно регулярное выражение для анализа того, что может быть несколькими получателями:

 $to = $req->post('to');
preg_match_all('/\b[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]+\b/i', $to, $result, PREG_PATTERN_ORDER);

foreach ($result[0] as $toAddress) {
    // handle the from address
}

Для каждого получателя мы затем извлекаем псевдоним части адреса и пытаемся найти пользователя, который соответствует:

 $alias = substr($toAddress, 0, strpos($toAddress, '@'));
$user = $c['db']->users->where('alias = ?', $alias)->fetch();

if ($user) {
    // great, we've got a valid user 
}

Для каждого получателя создание сообщения может выглядеть примерно так:

 $c['db']->posts()->insert(array(
    'title'   =>  $req->post('subject'),
    'body'    =>  ($req->post('html')) ? $req->post('html') : $req->post('text'),          
    'user_id' =>  $user['id'],
));

Теперь у нас есть базовые функции электронной почты! Конечно, это не очень безопасно, но это выходит за рамки этой статьи.

Давайте немного расширим приложение и позволим пользователям добавлять изображение в сообщение, отправляя его как почтовое вложение. Запрос POST от SendGrid включает параметр под названием attachments Прикрепленные файлы размещаются вместе с запросом и могут обрабатываться так же, как при загрузке файлов из веб-формы.

 if ($user) {
    $numAttachments = $req->post('attachments');
    $uploadedFiles = array();

    if ($numAttachments) {
        foreach ($_FILES as $key => $file) {
            $log->info('Saving file to ' . $c['config']['uploads.dir'] . $file['name']);
            move_uploaded_file($file['tmp_name'], $c['config']['uploads.dir'] . $file['name']);
            $uploadedFiles[] = $file['name'];
    }
}

Для простоты давайте просто сохраним имя файла первого прикрепленного файла в базе данных:

 $c['db']->posts()->insert(array(
    'title'   => $req->post('subject'),
    'body'    => ($req->post('html')) ? $req->post('html') : $req->post('text'),          
    'user_id' => $user['id'],
    'image'   => (count($uploaded_files)) ? $uploadedFiles[0] : ''
));

Резюме

В этой статье мы рассмотрели простое использование парсинга входящей электронной почты — базовая функция типа «электронная почта для публикации» для приложения в стиле блогов, позволяющая пользователям создавать сообщения, отправляя электронную почту на специальный адрес.

Регистрируя простой обратный вызов, вы можете делать разные забавные и интересные вещи, чтобы отвечать на входящие письма. Например, как насчет:

  • отправка оповещений при получении письма
  • передача вложений в Dropbox, Copy.com, SkyDrive, S3 и т. д.
  • позволяя людям отвечать по электронной почте на уведомления с досок объявлений, систем обмена сообщениями и т. д.
  • обработка запросов на отписку

Если вы думаете о других интересных применениях, дайте мне знать в комментариях. О, и загружаемый код для этой статьи доступен на GitHub для вас, чтобы клонировать и исследовать.