message_pusher
What it does
Message Pusher delivers Message Stack entities to subscribed clients in real time, using Pusher or any Pusher-protocol compatible server such as Soketi or pws.
It hooks into Message Notify as a Notifier plugin (id: pusher), so it slots in next to the email notifier and inherits all the standard message_notify behavior — save on success, save on fail, rendered view-modes, MessageNotifierBase::postSend().
Use cases
The transport is generic; anything that can be modeled as a Message entity can ride it:
- Notifications — mentions, replies, system alerts on
private-user.{uid}, rendered as bell popups. - Activity streams / timelines — "Alice posted on group X" on
group-{linked_entity_id}. - Chat / threads — chat lines on
thread-{linked_entity_id}. - Live dashboards — metric events on
dashboard-{uid}. - Broadcast announcements — owner-less Messages on a static channel like
announcements. Access checks adapt:{uid}is only required when the channel pattern actually uses it.
How it works
Channel resolution is token-driven. Default pattern is private-user.{uid} (matches the Pusher User convention). Available tokens:
{uid}— recipient user id{template}— message template machine name{linked_entity_type}/{linked_entity_id}— when those base fields exist on the Message entity
You can fan out one Message to multiple channels in a single trigger via additional_channels. The Pusher event name defaults to the message template machine name, but can be overridden per send. Optionally include the rendered pusher view-mode HTML in the payload (off by default — Pusher caps payloads at ~10kB and rendered HTML may leak privileged content into a channel).
Server side:
$message = Message::create([ 'template' => 'mention_news', 'uid' => $recipientUid, 'linked_entity_type' => 'node', 'linked_entity_id' => $node->id(), ]); $message->save(); \Drupal::service('message_notify.sender')->send($message, [], 'pusher');
Client side (with pusher_user installed, every authenticated page subscribes the current user to their own private-user.{uid} channel and re-broadcasts each event):
$(document).on('pusherUser:event', (e, eventName, data) => { if (eventName.startsWith('mention_')) { // Update bell counter, prepend to feed, refetch endpoint, etc. } });
For native clients (React Native, iOS, Android) subscribe to the same channel through your Pusher SDK after authenticating against pusher_api's /admin/pusher-api/authentication endpoint.
Requirements
- Message
- Message Notify
- Pusher API — must be configured via
$settings['pusher_api']insettings.php.
Recommended companion modules
- Pusher User — auto-subscribes logged-in Drupal users to their
private-user.{uid}channel on the web, so no extra JS is needed to receive notifications.
Installation
composer require drupal/message_pusher drush en message_pusher
See the bundled README.md for the full configuration reference, payload shape, and test instructions.
Self-hosting tip
Pusher.com works out of the box, but for in-house / EU-hosted real-time you can swap in Soketi — it speaks the Pusher protocol, runs in Docker, and requires no code changes on either the Drupal side or the client side. Just point $settings['pusher_api']['default']['options'] at your Soketi host.
Status
1.0.0-alpha1 — first release. Production-shaped but the API may still shift before 1.0.0 stable. Issue reports, MRs and feedback very welcome.
Related issues upstream
A couple of pusher_api issues are open that affect this module's robustness:
- pusher_api #3591837:
PusherService::trigger()should returnboolso callers can detect delivery failure. Once merged, this module'sdeliver()will pass real success/failure back toMessageNotifierBase::postSend()andsave on failwill behave correctly. - pusher_api #3591832: self-hosted servers via
js_options— needed if you're running Soketi instead of pusher.com and want the browser to connect to your own host.