Developers

Developer Guide: Hooks and Building Channel Add-ons

Published · Last updated

Super Speedy Chat is built so that new chat channels (WhatsApp, Telegram, SMS, …) can be added as separate plugins without touching core. The bundled Discord integration is itself written entirely against this extension surface – it’s the reference implementation. This guide documents the hooks, helpers, and registry you build against.

For the big-picture component map and the message lifecycle, read the Architecture Overview first. This guide is the practical hook reference.

The model in one paragraph

A conversation has many participants (typed visitor / admin / bot / system) and many messages, and carries a channel discriminator. Core never calls a channel directly – it fires lifecycle actions, and channels listen. To push messages in from an external channel, you call two SSC_Chat helpers. To appear in settings/REST/UI, you hook the registration filters/actions. That’s the whole API.

Lifecycle actions (core fires, you listen)

ActionArgsFires when
ssc_visitor_message_sent$message_id, $conversation, $message_text, $participantA visitor sends a message.
ssc_admin_reply_sent$message_id, $conversation, $message_text, $admin_user_idAn admin replies (from wp-admin; $admin_user_id is null for replies that originated in an external channel).
ssc_bot_message_sent$message_id, $conversation, $message_text, $message_typeAn auto/bot message is sent.
ssc_conversation_status_changed$conversation_id, $new_status, $old_statusStatus transitions (active/waiting/closed/archived).
ssc_conversation_created$conversationA new conversation row is created.

Example – relay every visitor message to your channel:

add_action( 'ssc_visitor_message_sent', function ( $message_id, $conversation, $text, $participant ) {
    if ( $conversation->channel !== 'mychannel' ) {
        return; // not ours
    }
    My_Channel::push( $conversation, $text );
}, 10, 4 );
Common mistake

Every lifecycle listener fires for all channels. Forget the $conversation->channel guard and your add-on will try to relay website chats, Discord threads, and every other channel’s traffic to your endpoint. Check the channel first, every time.

Inbound helpers (you call core)

When a message arrives from your channel, don’t write to the tables yourself – use these so participant creation, status transitions, and lifecycle hooks all happen correctly:

Best practice

Always route inbound messages through get_or_create_external_conversation() then external_inbound(). Writing directly to ssc_messages skips participant creation, the status state machine, and the lifecycle actions, which means the admin inbox, email fallback, and any other channel mirroring your traffic all silently break.

  • SSC_Chat::get_or_create_external_conversation( $args ) – resolves (or creates) a conversation owned by an external channel. There’s no browser cookie, so identity is derived as sha256('ext:' . $channel . ':' . $external_id).
 $conv = SSC_Chat::get_or_create_external_conversation( array(
      'channel'      => 'mychannel',
      'external_id'  => $sender_phone_or_id,   // stable per remote user
      'display_name' => $sender_name,
      'metadata'     => array( 'phone' => $sender_phone ),
 ) );
  • SSC_Chat::external_inbound( $args ) – appends an inbound message and fires the right lifecycle hook.
 SSC_Chat::external_inbound( array(
      'conversation_id' => $conv->id,
      'channel'         => 'mychannel',
      'author_name'     => $sender_name,
      'author_type'     => 'visitor', // or 'admin' / 'bot' / 'system'
      'message'         => $text,
      'external_msg_id' => $remote_id, // optional, for your dedup
 ) );

Registration hooks (appear in settings, REST, UI)

HookTypeUse it to
ssc_register_rest_routesaction ($rest)Register your own REST routes (e.g. an inbound webhook).
ssc_register_settingsactionAdd Settings API sections/fields.
ssc_settings_tabsfilter ($tabs)Add a settings tab (['label'=>…, 'order'=>…]).
ssc_sanitize_optionsfilter ($sanitized, $input)Sanitise your own keys in the shared ssc_options array.
ssc_channelsfilter ($channels)Declare your channel (['id'=>…, 'label'=>…, 'icon'=>…]).
ssc_frontend_configfilter ($config)Add keys to the front-end ssc_config blob.
ssc_enqueue_frontendaction ($ultra_ajax)Enqueue your own bubble JS after core’s.
ssc_conversation_sidebaraction ($conversation)Render an extra panel in the admin conversation sidebar.
ssc_conversation_reply_footeraction ($conversation)Render notices below the admin reply box.

Filters worth knowing

  • ssc_llm_daily_cap – integer site-wide daily ceiling on LLM classification calls (return 0 to disable the cap).
  • Front-end window.ssc.hooks – a tiny @wordpress/hooks-style API in the bubble:
    • actions: ssc.bubble.rendered, ssc.bubble.opened, ssc.bubble.closed, ssc.bubble.messageSent, ssc.bubble.messageReceived
    • filter: ssc.bubble.welcomeMessage
 window.ssc.hooks.addFilter('ssc.bubble.welcomeMessage', 'mychannel', function (msg) {
      return msg || 'Hi there!';
 });

Registering your add-on

Call SSC_Addons::register() on plugins_loaded (priority ≥ 20) so core can list you on the Status tab and gate on version compatibility:

add_action( 'plugins_loaded', function () {
    SSC_Addons::register( array(
        'slug'               => 'ssc-mychannel',
        'name'               => 'My Channel for Super Speedy Chat',
        'version'            => '1.0.0',
        'channel'            => 'mychannel',
        'requires_core'      => '1.08',
        'requires_addon_api' => '1.0',
        'plugin_file'        => __FILE__,
    ) );
}, 20 );

If your requires_addon_api is newer than core’s SSC_Addons::ADDON_API_VERSION, registration is refused and an admin notice is queued.

A channel add-on, end to end

  1. On plugins_loaded (≥20): SSC_Addons::register() and add your ssc_channels entry.
  2. Add settings via ssc_settings_tabs + ssc_register_settings + ssc_sanitize_options.
  3. Register an inbound webhook via ssc_register_rest_routes; in its handler, call get_or_create_external_conversation() then external_inbound().
  4. Listen on ssc_admin_reply_sent (and ssc_visitor_message_sent if you mirror both ways) to push outbound messages to your channel.
  5. Optionally add sidebar/footer panels and front-end behaviour.

Read includes/class-ssc-discord.php as the worked example – every step above is visible there.

Further reading

the component map and message lifecycle behind these hooks
the endpoint shapes your inbound webhook sits alongside
the bundled add-on that dogfoods this whole surface
a second channel built as an add-on against these same helpers

Comments

▲
▼
1/1