Better Messages Module
Hi, I’ve developed a module for Better Messages. Would it be possible to add an optional Custom Route field to the top menu Messages icon?
Currently, the icon appears to depend on Fluent Messaging being enabled. As I use Better Messages instead, the Messages icon is marked inactive.
Ideally, I’d like to enter a custom route such as /better-messages, which would keep the existing Messages icon active but open my module instead.
If a custom route is used, could there also be a way for the custom module to supply the unread badge count for the top Messages icon?
What kind of module? I’m considering Changing from Fluent messages so I’m very curious about any extra integration possibilities 🙂
Hi — good news on both counts: the plumbing you're describing already exists, and we've actually built a Better Messages integration ourselves.
Why the icon is inactive: that's the Fluent Messaging row, so it correctly hides without that plugin. But the app isn't limited to it — the module system is designed for exactly this: cleanly swapping out built-in messaging (or the cart) with your own provider, touching zero core code, so it survives every core update.
How your devs wire it (both docs ship with the app):
- The row — your companion plugin hooks the
tbc_ca_register_plugin_menu_itemsfilter with a registration for your module. SetslotGroup: 'messages'and it joins the same slot as the built-in Messages icon — in Customize, admins get a "Provided by" dropdown and one provider is live at a time, so your row becomes the Messages icon rather than sitting next to a dead one. Full reference:setup/docs/menus.html#plugin-registration. - The badge + tap — declare the row
kind: 'module'and add amenuItemsregistration in your app module's manifest withuseBadgeCount(a hook returning your live unread count) anduseOnPress. The badge is zone-neutral — admins can place the row in the header, tab bar, or launcher and the count follows it. Full reference:setup/docs/module-system.html.
So no "Custom Route field" is needed — a route override alone would still leave the badge wired to Fluent's counts, whereas the module registration gives you the count natively.
On Better Messages specifically: we've built our own add-on for it (companion plugin + app module — native inbox/threads, unread badge, push routing) on the free version of Better Messages. Candidly, it's had less real-world mileage than our Fluent-based features since most of our users run Fluent Messaging — so I'd be glad to give it to you free. Use it outright if it covers your needs, or as a working reference for your own module — and any feedback from real Better Messages usage would genuinely help us improve it for everyone.
here's the Better Messages add-on I mentioned: https://www.twobirdscode.com/blog/item/tbc-app-better-messages-addon/
One thing worth knowing: it's built against the free version of Better Messages, so message delivery is polling-based. The delivery layer is isolated behind its own interface though, so if you're on the paid tier you could build on top of it and swap in their websocket support for true live messaging — the add-on gives you the whole foundation (screens, state, unread badge, menu wiring) either way.
I’m using the Better Messages WebSocket version, so the standard bp-better-messages plugin isn’t required for Better Messages itself to work.
I’ve created my own Better Messages App companion plugin and Better Messages WebSocket app module, based on your original Better Messages add-on code, but adapted specifically for the WebSocket version.
The remaining issue is module activation. The module imports successfully and I get the “activated” confirmation, but the Activate button remains and the module doesn’t appear to stay registered as active.
I’m currently running Community App Core v1.3.13. Is there still an issue in the module activation/registry writer in this version, or is there something specific the module manifest/export needs for activation to persist?
The issue looks like the activation handler is returning success without successfully updating _registry.ts.
Managed to fix the issue, now at last stage but hit by this not being able to activate..
David Issue 1: companion plugin cannot be activated alongside the WebSocket edition.
The tbc-better-messages main file declares Requires Plugins: bp-better-messages. The standalone "Better Messages - WebSocket Version" installs under the slug bp-better-messages-websocket, so WordPress 6.5+ refuses to activate the companion plugin even though that edition provides the identical Better_Messages() runtime. I compared both editions' source: the WebSocket edition registers a strict superset of the better-messages/v1 REST routes (it adds calls and E2E routes), and every hook the companion uses (better_messages_rest_api_update_data, better_messages_push_active, better_messages_3rd_party_push_active, better_messages_message_sent) exists in both. Fix: remove the Requires Plugins header and rely on the existing function_exists('Better_Messages') runtime check, which already covers both editions. Cosmetic follow-up: the diagnostics report reads the version from WP_PLUGIN_DIR . '/bp-better-messages/bp-better-messages.php' only; prefer Better_Messages()->version and fall back to scanning both folder names.
Issue 2: module activation reports success without updating _registry.ts.
Reproduced in setup/lib/modules.js (toggleModule, activate branch). The array insert is anchored on /(export const MODULES: ModuleManifest\[\] = \[)([\s\S]*?)(\n\];)/, which requires the closing ]; on its own line. When the MODULES array sits on a single line, either a fresh registry with = []; or one collapsed by Prettier to = [someModule];, the replace silently no-ops. The import line IS inserted (its regex does match), the file is written, and the function still returns { ok: true, "activated" }. The module ends up in exactly the half-registered state getInstalledModules checks for (import present, array entry missing), so the dashboard keeps showing the Activate button while every activation attempt reports success. Fix: make the array insert handle single-line arrays (insert before the closing ] regardless of layout), and verify after the string operations that both the import line and the array entry are actually present before writing, returning a real error instead of success when they are not. With that patch applied, import, re-import, deactivate/reactivate round-trips, collapsed arrays and a corrupted registry all behave correctly in my tests against core 1.3.9.
Issue 3: License "Activate" button leads to a blank admin-post.php page.
TBC_Licenser::register() in tbc-better-messages.php is called without an action_prefix. The shared render_license_panel() then derives the form names from the slug: form action tbc_better_messages_activate_license, nonce tbc_better_messages_license_action, input tbc_better_messages_license_key. The plugin's settings class, however, registers its handlers under the short prefix: admin_post_tbc_bm_activate_license, nonce tbc_bm_license_action, input tbc_bm_license_key. All three names miss, so submitting the key posts to admin-post.php where no handler listens, and WordPress renders an empty admin page. Fix: pass 'action_prefix' => 'tbc_bm' to register() so the panel and the handlers agree. Recommended hardening: also register alias admin_post_tbc_better_messages_* hooks and accept both nonce/input naming schemes in the handlers, so the panel keeps working when an older copy of the shared licenser library (shipped by a neighbouring TBC plugin, first-load-wins) renders it without action_prefix support.
I was able to use the add on in combination with the Better Messages websocket version. Very cool and usefull! Thank you so much for making this available.
i’ve noticed a few minor things: the emoji reaction doesn’t work in combinstion with the multi-reactions add-on. And the avatar in the top bar seems to be missing.
David Nick , thanks to you both. I checked all three against our code
1. Module activation not sticking. Real core bug. Modules activate fine on an app that already has modules installed, which is why we never saw it. On a fresh app with an empty module list, the first activation adds the import line but misses adding the module to the list, and still reports "activated". That leaves it half-registered and the Activate button comes back. David, your read on this was correct. Fix goes into the next core update.
2. License button going to a blank page. Our bug in the Better Messages add-on. The license form and the code handling it weren't using the same name, so the form went nowhere. Fix goes into the next add-on release.
3. Plugin won't activate next to the WebSocket version. Our plugin header tells WordPress to require the free Better Messages folder, so WordPress blocks it before our own check runs. Our own check already handles both versions, so we'll remove that header line. Also in the next add-on release.
None of these need anything from you. They're next up and I'll post here when both updates are out.
Nick, great to hear it runs on the WebSocket version. Noted on the three things you spotted: emoji reactions not working alongside the multi-reactions add-on, the missing avatar in the top bar, and the missing message timestamps. That's exactly the kind of real-usage feedback we hoped for, so keep it coming.
One ask. The add-on will run on the WebSocket version after the fix, but it will still poll for messages like it does today. If either of you can send a copy of the WebSocket version, we can test against it properly and look at adding true live messaging to the add-on for people on the paid tier.
James William Elrod I’ve emailed over the latest BM Websocket, let us know