geekcodev/filament-max-broadcasts
| 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 |
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-client1.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
Filament plugin: operator chat with MAX messenger users, on top of geekcodev/lar...
A beautiful file manager plugin for Filament. Browse, upload, rename, move and d...
A modern CMS built on Laravel, Livewire, Alpine.js, and Filament. The TALL stack...
A comprehensive Laravel Filament 3 💡 starter kit with pre-installed plugins, ad...
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 | |
| v1.0.2 | ^8.4 | ^13.0 | MIT | |
| v1.0.1 | ^8.4 | ^13.0 | MIT | |
| v1.0.0 | ^8.4 | ^13.0 | MIT |