Download the PHP package ttbooking/mailspoon without Composer

On this page you can find all versions of the php package ttbooking/mailspoon. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package mailspoon

Mailspoon

tests Latest Stable Version

Простое реле IMAP → HTTP-вебхук, совместимое с Mailgun. Пакет для Laravel.

Mailspoon подключается к обычному IMAP-ящику, следит за появлением новых писем и пересылает каждое входящее письмо на HTTP-эндпоинт, используя тот же формат данных и схему подписи, что и входящие вебхуки Mailgun. Это позволяет продолжать обрабатывать почту привычным Mailgun-эндпоинтом (например, laravel-mailbox), даже когда письма приходят по обычному IMAP, а не через Mailgun.

Устанавливается composer-пакетом в любое приложение Laravel 13; чтение почты — на базе ImapEngine (directorytree/imapengine-laravel).

Как это работает

Mailspoon работает по схеме store-and-forward: чтение ящика отделено от доставки вебхука, поэтому медленный или недоступный эндпоинт не блокирует однопоточное чтение почты.

  1. Команда забирает непрочитанные письма из папки ящика (по умолчанию INBOX) и на каждое диспатчит событие MessageReceived из ImapEngine.
  2. Слушатель StoreIncomingMessage сохраняет сырой MIME в хранилище, создаёт запись о письме со статусом pending и сразу помечает письмо прочитанным — приём надёжно зафиксирован локально.
  3. Команда mailspoon:deliver независимо разбирает pending-записи и шлёт POST на эндпоинт. Успех → delivered; ошибка → attempts++ и failed, письмо переотправится на следующем запуске (до MAILSPOON_MAX_ATTEMPTS).

Дедупликация по Message-Id (или хешу письма, если заголовка нет) исключает повторную обработку одного и того же сообщения.

Содержимое вебхука

Запрос отправляется как application/x-www-form-urlencoded и содержит следующие поля, повторяющие входящий MIME-вебхук Mailgun:

Поле Описание
body-mime Полный исходный MIME-текст письма.
timestamp Unix-метка момента отправки вебхука.
token Случайный hex-токен длиной 50 символов, уникальный для каждого запроса.
signature HMAC-SHA256(timestamp + token, MAILSPOON_KEY) — проверяется на стороне получателя.

Проверяйте подпись на своей стороне так же, как для Mailgun: hash_hmac('sha256', $timestamp . $token, $signingKey).

Помимо полей формы запрос несёт служебные HTTP-заголовки:

Заголовок Описание
X-Mailspoon-Message-Id Message-Id письма (отсутствует, если у письма нет этого заголовка).
X-Mailspoon-Attempt Номер попытки доставки этой записи, начиная с 1.

Доставка — at-least-once. Успехом считается полученный ответ 2xx: если эндпоинт обработал запрос, но ответ потерялся (таймаут, обрыв), письмо будет отправлено повторно. Обработчики на принимающей стороне должны быть идемпотентными — заголовок X-Mailspoon-Message-Id позволяет отбросить дубликат ещё до разбора MIME.

Требования

Установка

Миграции пакета применяются автоматически; при желании их можно скопировать в приложение: php artisan vendor:publish --tag=mailspoon-migrations.

Диск архива: обязателен 'throw' => true

Архив .eml — единственная копия письма после пометки прочитанным, поэтому ошибки записи/чтения/удаления не должны подавляться Flysystem. Mailspoon отказывается работать с диском, у которого 'throw' => false (значение по умолчанию в свежем Laravel). Включите его для выбранного диска в config/filesystems.php:

Конфигурация

IMAP-подключение (config/imap.php)

Дополнительные необязательные переменные: IMAP_TIMEOUT, IMAP_DEBUG, IMAP_VALIDATE_CERT, IMAP_AUTHENTICATION, а также настройки прокси (IMAP_PROXY_SOCKET, IMAP_PROXY_USERNAME, IMAP_PROXY_PASSWORD, IMAP_PROXY_REQUEST_FULLURI).

В config/imap.php под ключом mailboxes можно описать несколько ящиков; встроенный называется default.

Адрес пересылки (config/mailspoon.php)

Маршрутизация ящиков (config/mailspoon.php)

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

Опции маршрута: endpoint и key (откат на глобальные), mark (маркер просмотра, см. «Ящики-люди»), filters (заменяют глобальные целиком, см. «Фильтрация писем») и enabled (см. «Приостановка ящика»).

Ящик без маршрута (или с частичным маршрутом) использует глобальные MAILSPOON_ENDPOINT/MAILSPOON_KEY; если маршруты заданы для всех ящиков, глобальные значения можно не задавать вовсе — mailspoon:doctor проверяет, что каждый ящик резолвится хоть во что-то. Эндпоинт фиксируется в записи в момент захвата письма, а ключ подписи выбирается в момент доставки — поэтому ротация ключа действует и на ещё не доставленные письма, а смена эндпоинта — только на новые.

Приостановка ящика (enabled)

Чтобы временно остановить или отложить чтение конкретного ящика, не удаляя его настройки, задайте в маршруте 'enabled' => false:

Это касается только захвата: mailspoon:pull сразу завершается, не подключаясь к IMAP и не двигая UID-курсор; mailspoon:sentry не запускает ни pull, ни IDLE-наблюдение; а cron-poll из schedule.pull для этого ящика не регистрируется в планировщике. Письма, уже сохранённые в архиве, продолжают доставляться mailspoon:deliver как обычно. По умолчанию ящик включён (флаг можно опустить). Прямой запуск imap:watch пакета imapengine-laravel флаг не проверяет.

Фильтрация писем (config/mailspoon.php)

Правила include/exclude применяются до захвата: отфильтрованное письмо помечается просмотренным, но не попадает ни в журнал, ни в архив, ни на эндпоинт. Карта filters — глобально или на маршруте (маршрутная заменяет глобальную целиком):

deny приоритетнее allow; пустой allow пропускает всё. Поля: from, subject, header, has_attachment. Кривое правило (битый регэксп, неизвестное поле) ловится на старте и в mailspoon:doctor, а не молча пропускает письма. Проверить правила на конкретном письме до запуска чтения — mailspoon:filter-test.

Каждое отфильтрованное письмо оставляет след: запись в логе Laravel и событие MessageFiltered — слишком строгое allow-правило видно по логу, а не по тишине в журнале.

Пример: пропускать только подтверждения о прочтении (MDN) — формат multipart/report с report-type=disposition-notification:

Ящики-люди: маркер просмотренного (mark)

По умолчанию mailspoon помечает обработанные письма прочитанными (\Seen — его курсор), что годится для ящика-робота. Если ящик читают люди (общий ящик операторов), прочитанность трогать нельзя — настройка mark глобально или на маршруте:

Тонкость стыка none × retention: дедуп-записи журнала живут MAILSPOON_RETENTION_DAYS дней. Если сервер сбросит UIDVALIDITY (переезд, пересоздание папки) после того, как записи о старых письмах уже вычищены, UID-курсор обнулится и эти письма будут захвачены и доставлены повторно — дедупу не с чем их сравнить. Ситуация редкая (нужны оба события сразу), но на ящике с mark: none и короткой retention стоит про неё помнить; защита на принимающей стороне — те же идемпотентные обработчики.

Маркер ставится всем просмотренным письмам, включая отфильтрованные — иначе они перечитывались бы каждым запуском; на эндпоинт уходят только прошедшие фильтр. Первый запуск на ящике с keyword:/none просмотрит весь ящик (а не только непрочитанное) — это сознательно: обрабатывается вся история, совпавшая с фильтром.

Хранилище и доставка (config/mailspoon.php)

Карты — в опубликованном конфиге

Структурные настройки (например, расписание cron-poll по ящикам) задаются обычным PHP в config/mailspoon.php — без сериализации в env:

Опубликованный конфиг должен сохранять полную структуру секций: merge с дефолтами пакета выполняется только по верхнему уровню.

Маршруты из хранилища (Mailspoon::register())

Если набор ящиков не статичен — например, приложение использует Mailspoon как читца почты и каждый клиент регистрирует свой ящик и вебхук через UI, — маршруты можно задавать в рантайме, не редактируя config/mailspoon.php. Фасад Mailspoon повторяет Imap из imapengine: метод register() добавляет или переопределяет маршрут для ящика.

Зарегистрированный маршрут имеет приоритет над одноимённым в конфиге и заменяет его целиком (не сливается). Опции — те же, что в карте routes, плюс необязательный schedule: ящик с расписанием попадает в cron-poll наравне с записями schedule.pull, так что новый ящик начинает опрашиваться без правки конфига. Семантика времени прежняя: endpoint фиксируется при захвате, key выбирается при доставке, mark/filters — при захвате.

Динамические IMAP-подключения (Imap::register())

Маршрут описывает доставку. Чтобы pull-режим тоже работал из хранилища, ящику нужно само IMAP-подключение: mailspoon:pull/:sentry соединяются через Imap::mailbox($name), который резолвит config/imap.php. Своей абстракции для этого не нужно — у imapengine есть публичный Imap::register($name, $config), кладущий подключение в его менеджер на время процесса. Регистрируйте подключение и маршрут рядом, в boot() своего сервис-провайдера:

Так покрыты все точки входа: mailspoon:pull (в т.ч. фоновый), mailspoon:sentry (регистрация в одном процессе переживает вложенные pull и vendor imap:watch) и mailspoon:deliver (к IMAP не обращается).

Что учесть:

Использование

Mailspoon предоставляет команды чтения (mailspoon:pull, mailspoon:sentry) и команду доставки (mailspoon:deliver). Аргумент mailbox — это имя ящика из config/imap.php (для встроенного используйте default). Необязательный аргумент folder выбирает папку, отличную от INBOX.

mailspoon:pull — разовая проверка

Забирает все текущие непрочитанные письма, сохраняет их и завершается.

Опции:

Подходит для запуска по расписанию (cron), когда долгоживущий процесс не нужен.

mailspoon:sentry — забрать накопившееся и следить дальше

Сначала один раз выполняет mailspoon:pull, чтобы сохранить накопившиеся письма, затем начинает следить за ящиком в реальном времени (через IMAP IDLE) и сохраняет письма по мере поступления. Это рекомендуемый способ запускать Mailspoon как постоянный воркер.

Опции:

Запускайте под супервизором процессов (systemd, Supervisor и т. п.), чтобы он перезапускался автоматически:

Команда imap:watch (только слежение, без предварительного разбора) предоставляется самим ImapEngine; mailspoon:sentry — это обёртка над mailspoon:pull + imap:watch.

Команды чтения только сохраняют письма (архив + запись pending) и помечают их прочитанными. Сама доставка на эндпоинт выполняется отдельно — командой mailspoon:deliver.

mailspoon:deliver — доставка сохранённых писем

Разбирает pending-записи (и ранее проваленные, у которых прошёл backoff и не исчерпан лимит попыток), читает сырой MIME из архива и шлёт подписанный POST на эндпоинт. Ретрай двухуровневый:

Так зависший или медленный эндпоинт никогда не тормозит чтение ящика.

Опции:

Команда — разовая (one-shot); запускать её периодически проще всего планировщиком (см. ниже), который уже вызывает mailspoon:deliver с withoutOverlapping().

mailspoon:replay — переотправка писем

Сбрасывает записи журнала обратно в pending — фактическую отправку выполнит ближайший запуск mailspoon:deliver (сырой MIME читается из архива, лезть в ящик заново не нужно). Счётчик попыток обнуляется, так что переотправляются и письма с исчерпанным лимитом.

Replay — явное действие оператора: дедупликация сознательно обходится, можно переотправить и уже доставленное письмо (например, после потери данных на стороне получателя).

mailspoon:doctor — диагностика конфигурации

Проверяет всю цепочку до запуска воркера: наличие таблицы журнала, запись и чтение на диске архива (включая обязательный 'throw' => true), эндпоинт и ключ каждого ящика (маршрут или глобальные), реальный IMAP-логин и доступность эндпоинта. Печатает образец подписи для сверки ключа с получателем (MAILBOX_MAILGUN_KEY у laravel-mailbox). Завершается ненулевым кодом при любой провальной проверке — удобно как preflight в деплое.

Отдельная проверка schedule сообщает cron-расписание ящика. Её отсутствие — не ошибка (ящик может читаться mailspoon:sentry или ручным mailspoon:pull), поэтому это предупреждение (строка !), а не провал: код возврата остаётся нулевым, в JSON-отчёте статус проверки — warn. Расписание проверяется и для паузнутого маршрута (enabled => false): doctor — preflight, его часто гоняют во время настройки, пока ящик ещё выключен, поэтому пауза без расписания тоже даёт предупреждение, а не прячет недонастройку.

По умолчанию эндпоинт только пробуется OPTIONS-запросом (без тестовой почты в принимающее приложение); --send отправляет полноценное подписанное письмо с заголовком X-Mailspoon-Doctor: true и требует ответа 2xx.

mailspoon:filter-test — сухой прогон фильтров

Прогоняет письмо через filters ящика без захвата: не ходит в журнал, не архивирует, не помечает письмо и ничего не доставляет. Печатает вердикт и правило, которое его решило — какой deny отбросил письмо или какой allow его пропустил. Удобно при настройке правил, до запуска чтения.

Работает и на выключенном ящике (enabled => false): вопрос «отфильтруется ли это письмо» одинаково валиден для маршрута, который ещё только настраивают. Команда — тонкая обёртка над сервисом FilterTester (см. ниже), так что тот же вердикт можно получить из дашборда. Матчер берётся живой — из конфига или рантайм-Mailspoon::register(), — поэтому результат точно совпадает с тем, что решит захват. --file ничего не требует от IMAP; --uid тянет письмо из ящика.

Вызов операций из приложения (сервисы)

За командами mailspoon:doctor/:replay/:deliver стоят сервисы, которые можно вызвать из своего кода (контроллера, Job'а) и получить структурный результат вместо текста консоли — удобно, когда ящики и маршруты заводятся динамически (см. «Маршруты из хранилища») и обслуживаются из веб-интерфейса. Mailspoon HTTP-слой не шипит: контроллеры и UI строит хост, ниже — примеры.

Каждый результат реализует Arrayable/JsonSerializable, поэтому годится прямо для response()->json().

На что обратить внимание:

События

Реле остаётся «тупой трубой»: оно не шлёт уведомлений и не строит метрик, но объявляет хост-приложению о двух ситуациях, которые иначе остались бы незамеченными. Оба события дублируются записью в лог Laravel, так что минимум наблюдаемости есть и без слушателей.

Подписка — штатными средствами Laravel, например уведомление о застрявшем письме:

Запуск и расписание

Mailspoon регистрирует свои задачи в планировщике хост-приложения. Если системный cron для schedule:run ещё не настроен, добавьте одну строку:

Что именно планируется, задаётся в config/mailspoon.phpschedule (все задачи — с withoutOverlapping()):

Отсюда два режима эксплуатации:

Режим Чтение Демон / supervisor Латентность
Cron-poll mailspoon:pull по карте schedule.pull не нужен = интервал cron
Realtime mailspoon:sentry (IMAP IDLE) под supervisor нужен для watcher секунды

В обоих режимах доставку выполняет запланированный mailspoon:deliver — отдельный демон или очередь для неё не требуются.

Связка с Laravel Mailbox

Mailspoon отлично сочетается с beyondcode/laravel-mailbox. Поскольку Mailspoon шлёт запрос в точности так же, как входящий MIME-вебхук Mailgun, приложение может принимать пересылаемые письма штатным mailgun-драйвером Laravel Mailbox — никакого кастомного кода для приёма не требуется. Mailspoon можно установить как в отдельное приложение-реле, так и прямо в приложение с Laravel Mailbox — тогда оно само читает свой ящик и шлёт вебхук на собственный эндпоинт.

В приложении-получателе с установленным Laravel Mailbox:

а в Mailspoon направьте реле на его эндпоинт и используйте тот же ключ, чтобы подписи совпадали:

Дальше обрабатывайте письма как обычно через маршруты Laravel Mailbox:

Итоговый поток: IMAP-ящик → Mailspoon → вебхук Mailgun → Laravel Mailbox → ваши обработчики.

Лицензия

Mailspoon распространяется по лицензии MIT.


All versions of mailspoon with dependencies

PHP Build Version
Package Version
Requires php Version ^8.3
directorytree/imapengine-laravel Version ^1.3
illuminate/console Version ^13.0
illuminate/container Version ^13.0
illuminate/contracts Version ^13.0
illuminate/database Version ^13.0
illuminate/events Version ^13.0
illuminate/filesystem Version ^13.0
illuminate/http Version ^13.0
illuminate/support Version ^13.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package ttbooking/mailspoon contains the following files

Loading the files please wait ...