geekcodev/filament-max-broadcasts

Filament plugin: mass broadcasts to MAX messenger users, on top of geekcodev/laravel-max-client.
41
Install
composer require geekcodev/filament-max-broadcasts
Latest Version:v1.1.1
PHP:^8.4
License:MIT
Last Updated:Oct 4, 2026
Links: GitHub  ·  Packagist
Maintainer: Evgeny Semenov

filament-max-broadcasts

Filament-плагин: массовые рассылки пользователям MAX-мессенджера внутри Filament-панели. Строится поверх geekcodev/laravel-max-client (реестр чатов max_chats/max_users) и geekcodev/max-php-client (API MAX).

Возможности:

  • ресурс «Рассылки» (BroadcastResource): создание, список, просмотр, relation manager получателей;
  • ресурс «Сегменты» (BroadcastSegmentResource): именованные группы чатов для целевого выбора получателей;
  • выбор получателей при создании — несколько сегментов и/или конкретные чаты с серверным поиском по chat_id (снимок recipient_chat_ids, повторы уходят тем же людям);
  • текст рассылки в формате HTML (RichEditor) с санитизацией под whitelist тегов MAX;
  • тип «Новость» или «Акция» (реестр типов в конфиге, добавляются свои); для типов с настроенными кнопками — кнопки-диплинки в мини-приложение;
  • медиа-вложения к рассылке — несколько картинок, видео и файлов (загрузка через FileUpload, типы и лимит из конфига) и отложенная отправка (scheduled_at);
  • сбор получателей — активные чаты из реестра max_chats (дедуп по chat_id, расширяемый резолвер);
  • opt-in согласие на рассылку: действие «Запросить согласие» рассылает callback-кнопки и складывает согласившихся в сегмент «Новости и акции»;
  • статусы scheduled → running → completed/cancelled/failed со счётчиками total/delivered/failed;
  • очередь SendBroadcastJob: лок на рассылку, батчи, ретраи, отмена, событие BroadcastCompleted;
  • действия «Повторить» / «Отправить сейчас» / «Отменить» / «Удалить», фильтры по статусу и типу;
  • права настраиваются строками (broadcasts.view / broadcasts.create / broadcasts.manage по умолчанию) — совместимо со spatie/laravel-permission и Gate.

Требования

  • PHP ^8.4, Laravel ^13.0
  • Filament ^5.0 (панель v5), Livewire ^4.1
  • geekcodev/laravel-max-client ^1.2.0 + geekcodev/max-php-client ^1.1.8
  • Опубликованные миграции laravel-max-client (max_users, max_chats, max_chat_users)
  • Работающая очередь (для фактической отправки) и общее для всех воркеров хранилище кэша (redis/database/memcached): джобы берут Cache::lock(); на file/array рассылку отправят два процесса параллельно, а счётчики разъедутся.

Плагин читает из реестра max_chats поля status, chat_type и last_activity_at. Формы реестра у версий ядра разные: 1.1.x хранит строку на пару пользователь-чат с unique(user_id, chat_id) и колонкой user_id, а 1.2 — строку на чат с chat_id первичным ключом, связями чат-пользователь в max_chat_users и колонкой title. Код плагина работает с обеими формами через адаптер Support\ChatRegistry, но поддерживается форма 1.2, поэтому минимум — laravel-max-client ^1.2.0.

Апгрейд с 1.1.x выполняйте строго в таком порядке:

composer require geekcodev/laravel-max-client:^1.2.0
php artisan migrate      # новая форма реестра: max_chats по chat_id + таблица max_chat_users
php artisan max:upgrade  # перенос данных реестра из старой формы в новую

Пропуск max:upgrade оставит max_chat_users пустой, и плагин не найдёт ни одного получателя. Проверить фактически установленную версию можно командой composer show geekcodev/laravel-max-client.

Колонки max_broadcast_recipients.chat_id и user_id в плагине знаковые: идентификаторы групп и каналов MAX отрицательные, и без этого MySQL отвергал бы рассылку в канал, а на PostgreSQL тип bigint уже знаковый, и миграция 0000_03_000007 приводит его к тому же виду.

Миграции пакета лежат в полосе 0000_03 — это позиция пакета в общем порядке миграций, который Laravel строит по именам файлов. Полоса выбрана по графу внешних ключей: max_broadcasts.created_by ссылается на users, поэтому пакет применяется после системных миграций приложения, а laravel-max-client с max_users и max_chats занимает 0000_00. Полная раскладка:

0000_00 — laravel-max-client: max_users, max_chats, max_chat_users
0000_01 — приложение, системные таблицы (users и прочие)
0000_02 — filament-max-chat
0000_03 — filament-max-broadcasts
0000_04 — приложение, доменные таблицы

Требование к хосту: его системные миграции, создающие users, должны иметь имена меньше 0000_03_, иначе пакет применится раньше них, и внешний ключ на users упадёт на MySQL и PostgreSQL (на SQLite такая ошибка не всплывает). Дефолтные имена Laravel вида 0001_01_01_000001_create_users_table этому условию не удовлетворяют: переименуйте системные миграции приложения в полосу 0000_01_ до установки пакета. Проверить фактический порядок можно командой php artisan migrate:status — она перечисляет миграции в том же порядке, в котором они применяются, то есть по именам файлов.

Установка

composer require geekcodev/filament-max-broadcasts
php artisan migrate    # миграции max_broadcasts, max_broadcast_recipients, max_broadcast_attachments,
                       # max_broadcast_segments, max_broadcast_consents и pivot max_broadcast_segment
                       # загружаются из пакета автоматически

Подключение к панели:

use GeekCo\FilamentMaxBroadcasts\FilamentMaxBroadcastsPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->plugin(FilamentMaxBroadcastsPlugin::make());
}

Права (пример со spatie/laravel-permission):

Role::findByName('admin')->givePermissionTo([
    'broadcasts.view', 'broadcasts.create', 'broadcasts.manage',
]);

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

php artisan vendor:publish --tag=filament-max-broadcasts-config   # config/filament-max-broadcasts.php
php artisan vendor:publish --tag=filament-max-broadcasts-lang     # lang/vendor/filament-max-broadcasts
php artisan vendor:publish --tag=filament-max-broadcasts-migrations # миграции

Ключевые параметры:

Ключ По умолчанию Описание
permissions.* broadcasts.view/create/manage Права на доступ/создание/управление рассылками
types пакетные News / Promo enums Реестр типов рассылок: token → класс, реализующий BroadcastTypeContract
bot_username / buttons.per_type пусто ('') / [] Имя бота и кнопки-диплинки по типу по умолчанию (https://max.ru/<bot>?startapp=…)
queue.batch_size / lock_ttl_seconds / tries / timeout / backoff 25 / 600 / 3 / 3600 / [60,300] Параметры очереди SendBroadcastJob
image.disk / directory / max_kb / accepted_mime_types public / broadcasts / 51200 (КБ, ~50 МБ) / … Диск, каталог, лимит размера и допустимые типы медиавложений рассылки
chats_model пакетный Models\MaxChat Модель реестра чатов (переопределяйте подклассом)
broadcast_model / recipient_model / segment_model / user_model пакетные модели; Illuminate\Foundation\Auth\User Переопределение моделей плагина
consent.* сегмент «Новости и акции», текст запроса, кнопки Opt-in согласие: модель, имя сегмента, кнопки, тексты подтверждения opt-in/opt-out
recipients.resolver пакетный BroadcastRecipientsResolver Класс выбора получателей для рассылки
ui.* см. конфиг Иконка/лейблы/sort/slug навигации ресурса

Настройки кнопок-диплинков по умолчанию (по типу рассылки):

'bot_username' => 'my_service_bot',
'buttons' => [
    'per_type' => [
        'promo' => [
            ['text' => 'Запись на сервис', 'startapp' => 'booking'],
            ['text' => 'Консультация',     'startapp' => 'consult'],
        ],
    ],
],

Кнопок нет для типа, если bot_username пуст, для него ничего не настроено в buttons.per_type, либо тип переопределил buttonRows() (по умолчанию 'news' идёт без кнопок). Свои типы добавляются в реестр types классом, реализующим Contracts\BroadcastTypeContract (простейший — трейт Support\BroadcastTypeDefaults + регистрация в конфиге). Поведение типа (подписи, кнопки, цвет badge) живёт в самом типе.

Архитектура

  • Services\BroadcastService — создание рассылки: резолв получателей (явные чаты → сегменты → все активные), запись Broadcast + BroadcastRecipient, привязка сегментов, dispatch() очереди (с delay() при будущем расписании);
  • Services\BroadcastRecipientsResolver — единственный источник списка получателей (активные чаты max_chats, дедуп по chat_id, сортировка по last_activity_at);
  • Models\BroadcastSegment + Services\ConsentService — именованные сегменты чатов; сегмент «Новости и акции» — источник согласившихся на рассылку (факты opt_in/opt_out в max_broadcast_consents);
  • Services\ConsentRequestService + Jobs\SendConsentRequestsJob — действие «Запросить согласие»: кандидаты — активные чаты без ответа, рассылка фиксированного сообщения с callback-кнопками;
  • Listeners\HandleConsentCallback — приём ответов (событие MaxUpdateReceived), запись факта и ответ sendAnswer;
  • Services\BroadcastTextSanitizer — санитизация HTML под whitelist тегов MAX + toMaxHtml() (разворачивание <p>/<div>/<br> в \n, иначе MAX не рендерит абзацы);
  • Services\BroadcastSender — единая точка отправки в MAX: медиавложения (картинки/видео/файлы через uploadMedia), кнопки-диплинки (InlineKeyboard), сообщение с TextFormat::Html;
  • Support\BroadcastTypes — реестр типов из конфига (types, token → класс контракта) для форм/фильтров/колонок: options(), instance(), label(), badgeColor();
  • Contracts\BroadcastTypeContract + Support\BroadcastTypeDefaults — типы как поведение: каждый тип — свой backed-enum, подписи/кнопки/цвет определяет сам тип (дефолты — из трейта, кнопки по умолчанию из bot_username + buttons.per_type);
  • Jobs\SendBroadcastJob — очередь: Cache::lock("broadcast:{id}"), статус running, батчи по queue.batch_size с проверкой отмены и обновлением счётчиков, по завершении — completed + BroadcastCompleted;
  • Events\BroadcastCompleted — событие завершения рассылки;
  • Модели Models\Broadcast (max_broadcasts) / Models\BroadcastRecipient (max_broadcast_recipients) с конфигурируемыми связями creator() → user_model, maxChat() → chats_model.

Прямые вызовы ApiClient из Filament-страниц запрещены — отправка только через BroadcastSender. Текст повторно санитизируется перед каждой отправкой.

Приём получателей

Получатели собираются серверно из реестра max_chats — из формы рассылки пользователь не может подставить произвольные чат-идентификаторы. Выбор при создании: несколько сегментов (BroadcastSegment, union их chat_ids) и/или ручная корректировка поиском активных чатов (клиент подгружает только найденные); фактический список получателей сохраняется снимком в recipient_chat_ids (пусто — всем активным). Действия «Повторить» отправляют рассылку тому же снимку, поэтому повтор уходит тем же людям, даже если состав сегментов изменился. Чтобы расширить базовый источник чатов (фильтры, исключения, целевые группы), замените класс резолвера через recipients.resolver — он должен реализовывать контракт resolve(): Collection<int, MaxChat>.

Согласие на рассылку (opt-in)

На списке рассылок есть действие «Запросить согласие» (permissions.create): оно ставит в очередь рассылку фиксированного сообщения с callback-кнопками «Согласен»/«Не согласен» всем активным чатам, ещё не ответившим. Ответы принимаются слушателем на webhook (HandleConsentCallback), факт записывается в max_broadcast_consents, а согласившиеся попадают в сегмент «Новости и акции» (consent.segment_name) — по нему можно делать целевые рассылки. Факт получения запроса не фиксируется: проигнорировавшие получат запрос снова при следующем запуске, ответившие — никогда.

Тестирование и разработка

PHP/Composer на хосте не требуются — всё через Docker (образ PHP 8.4, Orchestra Testbench):

docker compose up -d --build   # контейнер app (PHP 8.4)
docker compose run --rm app composer install
docker compose exec -T app composer test           # PHPUnit (SQLite in-memory)
docker compose exec -T app composer analyse        # PHPStan level max (Larastan + baseline)
docker compose exec -T app composer lint           # PHP-CS-Fixer (--dry-run)
docker compose exec -T app composer format         # PHP-CS-Fixer (исправить)
docker compose exec -T app composer coverage        # PHPUnit + отчёт покрытия и проверка порога строк (95%)
docker compose exec -T app composer security-audit # composer audit

Тот же набор прогоняет CI (.github/workflows/ci.yml) на PHP 8.4 с Xdebug в один job quality. Отчёт покрытия требует Xdebug: локально в dev-образе он включён, в CI — через coverage: xdebug и XDEBUG_MODE=coverage.

История изменений

Полные release notes по версиям — в каталоге .agents/release/ репозитория (в архив пакета он не попадает), здесь только значимое для пользователя.

  • v1.1.1 — миграции пакета переведены в полосу 0000_03, то есть их позиция в общем порядке миграций выведена из графа внешних ключей, а не из порядка установки плагинов. Переименование безопасно для существующих установок: в up() шести create-миграций стоит Schema::hasTable(), поэтому php artisan migrate на обновлении проходит no-op. Требуется только соблюсти требование к именам миграций приложения, описанное выше.
  • v1.1.0 — переход на форму реестра laravel-max-client 1.2: минимум laravel-max-client ^1.2.0 и max-php-client ^1.1.8, адаптер Support\ChatRegistry для обеих форм реестра, знаковые chat_id/user_id в таблице получателей. При обновлении с 1.1.x: php artisan migrate, затем php artisan max:upgrade.
  • v1.0.5 — в таблице согласий имя и тип чата (бейдж «Диалог»/«Группа»/«Канал», имя из профиля max_users, поиск по имени), без N+1.
  • v1.0.4 — read-only ресурс «Согласия» (BroadcastConsentResource) с журналом ответов и фильтром по действию; сегменты принимают пустой список чатов.
  • v1.0.3 — выбор получателей: список чатов подгружается при открытии, метки с бейджем типа и именем, поиск по имени и chat_id; ответ на согласие убирает кнопки из сообщения и подтверждает выбор в чат.
  • v1.0.2 — сегменты получателей и их ресурс; выбор получателей сегментами и конкретными чатами со снимком recipient_chat_ids; сбор opt-in согласия через callback-кнопки.
  • v1.0.1 — обновление пакетов laravel-max-client/max-php-client; просмотр вложений на странице рассылки.
  • v1.0.0 — первый релиз: ресурс «Рассылки», отложенная отправка очередью, медиавложения, типы рассылок с кнопками-диплинками, санитизация текста.

Related Packages

geekcodev/filament-max-chat

Filament plugin: operator chat with MAX messenger users, on top of geekcodev/lar...

65 0
mmes-design/filament-file-manager

A beautiful file manager plugin for Filament. Browse, upload, rename, move and d...

4,698 16
tallcms/tallcms

A modern CMS built on Laravel, Livewire, Alpine.js, and Filament. The TALL stack...

784 74
mwguerra/web-terminal

A web-based terminal component for Filament/Laravel with command whitelisting an...

19,298 30

Version History

Version Released PHP Laravel License
v1.1.1 ^8.4 ^13.0 MIT
v1.1.0 ^8.4 ^13.0 MIT
v1.0.5 ^8.4 ^13.0 MIT
v1.0.4 ^8.4 ^13.0 MIT
v1.0.3 ^8.4 ^13.0 MIT