Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Max Chat Laravel Package

geekcodev/filament-max-chat

Filament plugin for an operator chat with MAX messenger users. View dialogs with unread badges, reply with HTML formatting and media attachments, receive real-time Echo/Reverb updates with a poll fallback, and configure permissions, channel, disk and navigation via config.

View on GitHub
Deep Wiki
Context7
v1.1.5

v1.1.5

Патч добавляет индекс под подсчёт непрочитанных сообщений. Бейдж навигации панели считает непрочитанные входящие на каждой отрисовке страницы, а подходящего индекса у max_chat_messages не было — счёт шёл полным сканом таблицы. На достаточно большой переписке скан длился секунды, воркеры Octane оказывались заняты, healthcheck /up не успевал и контейнер перезапускался. Ломающих изменений нет.

Что было сломано

MaxMessageService::totalUnreadCount() и UnreadCountController выбирают where direction = 'in' and read_at is null, а markRead() — ту же пару условий с ведущим max_chat_id. Существующие индексы таблицы (max_chat_id, created_at), (user_id, chat_id, created_at) и (created_at) такую выборку не покрывают: подсчёт непрочитанного шёл последовательным сканом max_chat_messages. Пока строк мало, это незаметно; когда история вырастает, каждая отрисовка страницы панели занимает воркер на время скана. Поток запросов (например, при переключении вкладок) исчерпывает пул воркеров, и контейнер становится unhealthy.

Новое

Составной индекс (direction, read_at). Отдельная аддитивная миграция 0000_02_000003_add_unread_index_to_max_chat_messages.php создаёт индекс max_chat_messages_unread_index. Он покрывает и бейдж навигации, и JSON-контроллер непрочитанного; markRead() продолжает работать по существующему индексу с ведущим max_chat_id. Частичный индекс (where read_at is null) был бы компактнее, но он не переносим на MySQL, поэтому выбран обычный составной. Миграция идемпотентна: в up() стоит guard Schema::hasIndex, поэтому повторный запуск и установка, где такой индекс уже добавлен приложением-хостом, не падают.

Обновление установки — обычный php artisan migrate: ничего переносить не нужно, новых переменных окружения и ключей перевода нет.

Изменение (BC)

Ломающих изменений нет: публичные сигнатуры, ключи и значения по умолчанию конфига не менялись, ключи перевода не затрагивались. Добавлена только миграция.

Затронутые сценарии

  • Бейдж непрочитанного в навигации панели и JSON-счётчик непрочитанного: подсчёт идёт по индексу, а не полным сканом.
  • Отметка чата прочитанным: без изменений в поведении, работает по существующему индексу.
  • Установка и обновление: новая миграция применяется migrate; на SQLite, PostgreSQL и MySQL.

Качество

  • MaxChatMessagesSchemaTest проверяет наличие индекса max_chat_messages_unread_index, его удаление в down() и повторное создание в up(), а также идемпотентность (up() при уже существующем индексе не падает).
  • PHPUnit: 271 тест / 628 assertion, покрытие 98.31% строк (989/1006). PHPStan level max и PHP CS Fixer — без ошибок.
  • Локальный Gate: lint 0 · PHPStan 0 ошибок · 271 тест · покрытие 98.31% · composer audit без предупреждений (с COMPOSER_IPRESOLVE=4, иначе packagist недоступен — gotcha 11). Шаг Gate 7 (чистый резолв в .ci-sim/) в этой сессии не прогонялся: на диске хоста не осталось места под второй vendor/ — шаг остаётся за CI.
v1.0.7

v1.0.7

Переименование кластеров под laravel-max-client v1.1.0

Пакет geekcodev/laravel-max-client в версии 1.1.0 переименовал реестр чатов: модель BotChat → MaxChat, таблица max_bot_chats → max_chats, enum BotChatStatus → MaxChatStatus. Плагин приведён в соответствие: весь кластер Chat* переименован в Max*, а конфиг-ключ модели диалога заменён с bot_chat_model на chat_model.

Внешние ключи и JSON-контракты тоже переименованы: колонка bot_chat_id в max_chat_messages теперь max_chat_id, а поле payload счётчика непрочитанного — latest_max_chat_id вместо latest_bot_chat_id. При обновлении существующей установки перенесите данные реестра и переименуйте внешний ключ по инструкции в README («Обновление с v1.x»).

Это breaking change публичного API: host-приложения должны обновить импорты (BotChat → MaxChat, ChatMessageService → MaxMessageService и т.д.).

Аватары и профили пользователей

Раньше в апдейтах MAX аватар не приходит, и аватары в списке диалогов и в открытом чате не показывались. Теперь плагин фоново подгружает профиль пользователя через MaxUserProfileService клиента (getChatMembers): при открытии чата и для списка бесед ChatProfileRefresher решает, кому это нужно (не чаще, чем раз в cache_ttl), и ставит в очередь RefreshChatProfilesJob. Сам UI при этом не блокируется — аватар появляется после фоновой подгрузки. Управляется блоком profile.*; при profile.enabled=false вызовы к API полностью отключены.

Ссылка на конкретный чат

На страницу чата можно вести прямую ссылку с других страниц проекта: /admin/chat?chat_id=<id чата в MAX> открывает конкретный диалог (плагин сам находит запись реестра по chat_id). Прежний способ по внутреннему ID записи (?chat=<id max_chats>) сохранён для обратной совместимости. Если chat_id не найден — страница открывается как обычно, без активного диалога.

Удаление чата из списка

Добавлена кнопка «Удалить чат» (в шапке активного диалога, рядом с «Очистить историю»). В отличие от очистки истории, действие помечает запись реестра статусом removed — диалог исчезает из списка оператора, а история сообщений сохраняется. Если пользователь снова напишет, storeIncoming вернёт записи статус active — чат снова появится в списке, и оператор не пропустит обращение.

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

Новые опции в config/filament-max-chat.php:

Ключ Что делает По умолчанию
chat_model Модель диалога (вместо bot_chat_model) MaxChat
profile.enabled Включить фоновую подгрузку профилей/аватаров true
profile.prefetch Когда подгружать: on_open / on_list / both both
profile.cache_ttl Не перезапрашивать профиль одного пользователя (сек) 86400
profile.refresh_existing Обновлять ли профиль, если аватар уже заполнен false

Тесты

161 тест, 346 assertions. PHPStan level max, PSR-12, declare(strict_types=1).


Laravel · Filament · laravel-max-client

v1.0.6

v1.0.6

Исправления

  • Счётчик непрочитанных и уведомления теперь работают на всех страницах админки. Раньше обновление бейджа и звук со toast/browser-уведомлением приходили только двух путях: событие Echo chat-message.created и DOM-событие chat-unread, которое диспатчит Livewire-компонент лишь на странице чата. На остальных страницах панели не было ни одного периодического запроса, и счётчик в сайдбаре не обновлялся, пока вы не перезагрузите страницу. Добавлен глобальный HTTP-poll: новый эндпоинт GET /admin/chat/unread-count (UnreadCountController) отдаёт JSON {unread_count, latest_bot_chat_id}, а скрипт уведомлений опрашивает его на всех страницах и обновляет бейдж.

  • Звук/toast больше не срабатывают на собственные исходящие сообщения. Раньше уведомление появлялось на каждое событие Echo, включая ответы оператора. Теперь оповещение срабатывает только когда счётчик непрочитанных вырос — собственные отправленные сообщения его не увеличивают.

  • Двойные уведомления от Echo и poll исключены. Единая функция обработки сверяет последнее известное значение счётчика и играет сигнал только при его росте, поэтому событие real-time и периодический опрос не дублируют показ.

Улучшения

  • Подавление для «активного чата» распространяется и на poll: если последнее непрочитанное сообщение пришло из открытого сейчас диалога, звук не играет (вы и так его видите), но бейдж обновляется.

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

Новые опции в config/filament-max-chat.php:

Ключ Что делает По умолчанию
route.unread_count_uri Путь эндпоинта счётчика непрочитанного /admin/chat/unread-count
notifications.poll_enabled Включить HTTP-poll фолбэк к Echo true
notifications.poll_interval_seconds Интервал опроса 15

Тесты

144 теста, 317 assertions. PHPStan level max, PSR-12, declare(strict_types=1).


Laravel · Filament · laravel-max-client

v1.0.5

v1.0.5

Исправления

  • Счётчик непрочитанных в сайдбаре снова отображается. restoreBadge() на каждой загрузке страницы читал значение из localStorage (пустое → 0) и убирал нативный бейдж, который рендерит Filament-sidebar через getNavigationBadge(). Теперь при инициализации счётчик берётся из серверного бейджа сайдбара, так что он больше не стирается.

  • Уведомления перестали молчать после просмотра диалога. Подавление для «активного чата» держало bot_chat_id последнего открытого диалога в глобальной переменной и продолжало глушить звук/toast/browser-уведомление на любых других страницах админки. Теперь __fmcActiveChatId сбрасывается при уходе со страницы чата — уведомления снова приходят, даже если сообщение пришло из последнего просмотренного диалога.

  • Селектор бейджа соответствует конфигурируемому slug. Поиск пункта меню теперь строится по слагу из конфига (ui.slug), а не по жёстко зашитому /chat.

Улучшения

  • Бейдж обновляется в реальном времени. После прочтения диалога, удаления или очистки сообщений Livewire диспатчит событие chat-unread с актуальным общим числом непрочитанных — счётчик в сайдбаре синхронизируется сразу, не дожидаясь следующего события или перезагрузки.
v1.0.4

v1.0.4

Исправления

  • Бейдж уведомлений не появлялся. CSS-селектор a[href$="/chat"] не совпадал с DOM Filament v5 — ссылка в sidebar использует класс .fi-sidebar-item-btn. Селектор исправлен, бейдж теперь создаётся и обновляется корректно.

  • Кнопка «Убрать файл» появлялась во время загрузки. wire:model устанавливает $attachment сразу при выборе файла, и кнопка была видна до завершения загрузки. Добавлены Alpine + Livewire upload events (upload:started / upload:finished): кнопка скрыта через x-show="!uploading" пока идёт загрузка, после завершения — появляется.

  • Очистка file input после отправки. Событие clear-file-input диспатчится из Livewire после reset('attachment') и слушается в Blade для очистки DOM-значения инпута.

v1.0.3

v1.0.3

Нововведения

  • Аватар пользователя в заголовке чата. Рядом с именем отображается круглая аватарка (фото или инициалы) — как в списке диалогов.

  • Модалка с информацией о пользователе. По клику на имя в заголовке открывается Alpine-модалка: по центру большая аватарка, Имя Фамилия, ID пользователя. Закрывается по клику на backdrop, кнопке «Закрыть» или Escape.

Улучшения

  • Кнопка удаления сообщения перенесена внутрь пузыря сообщения (прижата к верхнему правому углу). Иконка заменена на серую корзину (heroicon-o-trash) — визуально единообразна с кнопкой очистки всего чата. При наведении плавно проявляется и становится красной.

  • Badge уведомлений теперь рендерится нативно в Filament sidebar через .fi-badge вместо кастомного <span>.

  • Подавление уведомлений в активном чате. Звук, toast и browser notification не срабатывают, если оператор уже открыл диалог, из которого пришло сообщение.

  • Отложенный запрос разрешения на уведомления. Notification.requestPermission() вызывается при первом клике пользователя, а не при загрузке страницы.

v1.0.2

v1.0.2

Улучшения

  • Broadcast-канал переименован из operator.chat в chat.channel — нейминг больше не привязан к роли оператора.

  • Бейдж уведомлений в sidebar. Badge теперь рендерится нативно в Filament sidebar (.fi-badge) вместо кастомного <span>, что делает его визуально единообразным с остальными бейджами панели.

  • Подавление уведомлений в активном чате. Если оператор уже открыл диалог, из которого пришло сообщение — звук, toast и browser notification не срабатывают. Отслеживание активного чата через dispatch('chat-active') в Livewire и window.__fmcActiveChatId на клиенте.

  • Отложенный запрос разрешения на уведомления. Браузерный Notification.requestPermission() вызывается при первом клике пользователя, а не при загрузке страницы — исключает блокировку браузером за незапрошенный弹窗.

Исправления

  • Сброс file input после отправки. Инпут файлов очищался серверно, но продолжал отображать имя файла в браузере. Добавлен wire:ignore + Alpine x-effect для корректной очистки DOM.

  • Кнопка удаления сообщения не появлялась. Кнопка × была скрыта через hidden, который перебивал group-hover:flex. Заменена на opacity-0 + pointer-events-none с плавным появлением при наведении.

  • Заголовок страницы. «Чат оператора» / «Operator chat» переименован в «Чат» / «Chat».

v1.0.1

v1.0.1

Исправления

  • Сброс file input после отправки. После отправки вложения инпутов очищался на сервере, но продолжал отображать имя файла в браузере. Добавлен wire:ignore и Alpine x-effect для корректной очистки DOM.

  • Кнопка удаления сообщения не появлялась. Кнопка × на сообщениях была скрыта через hidden (display: none), который перебивал group-hover:flex. Заменена на opacity-0 + pointer-events-none с плавным появлением при наведении.

  • Заголовок страницы. «Чат оператора» / «Operator chat» переименован в «Чат» / «Chat».

v1.0.0

v1.0.0

Filament v5 плагин для общения оператора с пользователями MAX-мессенджера.

Что внутри

Страница /admin/chat — лента сообщений, список диалогов, аватарки с инициалами. Оператор может отвечать текстом с HTML-разметкой или отправлять файлы (фото, видео, аудио, документы).

Историю можно почистить целиком или удалить отдельные сообщения.

Лента обновляется в реальном времени через Echo/Reverb. Если чат открыт на другой вкладке — появится сразу.

На остальных страницах админки работает через пуш:

  • бейдж с числом непрочитанных на ссылке «Чат» в сайдбаре
  • browser notification
  • звуковой сигнал (Web Audio API, без внешних файлов)

Вложения хранятся на приватном диске, отдаются по защищённому роуту. HTML-разметка от оператора фильтруется по белому списку тегов MAX.

Требования

  • PHP ^8.4
  • Laravel 13 / Filament 5
  • geekcodev/laravel-max-client с опубликованными миграциями

Установка

composer require geekcodev/filament-max-chat
php artisan vendor:publish --tag=filament-max-chat-config

Подключить плагин в AdminPanelProvider:

use GeekCo\FilamentMaxChat\FilamentMaxChatPlugin;

$panel->plugin(FilamentMaxChatPlugin::make());

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

Все опции в config/filament-max-chat.php:

Ключ Что делает По умолчанию
permissions.view Кто видит чат chat.view
permissions.answer Кто может отвечать chat.answer
broadcast_channel Канал Echo operator.chat
attachments.disk Диск для хранения файлов local
notifications.enabled Пуши на других страницах true
notifications.sound Звук при новом сообщении true
notifications.browser Browser notification true

Тесты

135 тестов, 96% покрытие строк. PHPStan level max, PSR-12, declare(strict_types=1).


Laravel · Filament · laravel-max-client

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
adachsoft/php-code-style