Pusher Client Delphi Update

The TsgcWS_API_Pusher component has been updated to align with the latest Pusher Channels protocol specification. This release introduces support for private-encrypted channels, four new presence and cache channel events, two new REST API endpoints, enhanced query parameters for existing endpoints, and several bug fixes. All changes are backward-compatible — existing code continues to work without modification.

1. Private-Encrypted Channel Support

Pusher Channels supports private-encrypted channels that provide end-to-end encryption of event data using NaCl Secretbox (XSalsa20-Poly1305). The Pusher servers never see the plaintext — only the communicating clients can decrypt the messages. Two new channel types have been added to the TsgcWSPusherChannels enumeration:

Enum Value Channel Prefix Description
pscPrivateEncryptedChannel private-encrypted- End-to-end encrypted private channel. Data payloads are encrypted with a per-channel shared secret.
pscPrivateEncryptedCacheChannel private-encrypted-cache- Same as above, plus cache channel behavior — the last event is delivered to new subscribers.

The TsgcWSPusherResponseAuthentication class now includes a SharedSecret property. When subscribing to a private-encrypted- channel, your authorization endpoint must return the base64-encoded per-channel shared secret. Set this value in the OnPusherAuthentication event handler:

procedure TForm1.sgcPusherAuthentication(Sender: TObject;
  AuthRequest: TsgcWSPusherRequestAuthentication;
  AuthResponse: TsgcWSPusherResponseAuthentication);
begin
  // For private-encrypted channels, set the shared secret
  // returned by your authorization server
  AuthResponse.Secret := 'your-app-secret';
  AuthResponse.SharedSecret := 'base64-encoded-per-channel-key';
end;

Subscribing to an Encrypted Channel

// Subscribe to a private-encrypted channel
sgcPusher.Subscribe('my-secret-channel', pscPrivateEncryptedChannel);

// Subscribe to a private-encrypted cache channel
sgcPusher.Subscribe('my-secret-cache', pscPrivateEncryptedCacheChannel);

Publishing on an Encrypted Channel

// Client events work on private-encrypted channels just like private channels
sgcPusher.Publish('my-event', 'my-secret-channel',
  pscPrivateEncryptedChannel, '{"message":"Hello encrypted!"}');

2. New WebSocket Events

Four new events have been added to handle Pusher internal protocol messages that were previously unhandled. These events are dispatched automatically when the corresponding pusher_internal: messages arrive over the WebSocket connection.

Event Property Protocol Message Description
OnPusherMemberAdded pusher_internal:member_added Fired when a new user joins a presence channel. Provides the channel name, user ID, and user info.
OnPusherMemberRemoved pusher_internal:member_removed Fired when a user leaves a presence channel. Provides the channel name, user ID, and user info.
OnPusherSubscriptionCount pusher_internal:subscription_count Fired when the subscription count for a channel changes. Must be enabled in the Pusher dashboard.
OnPusherCacheMiss pusher_internal:cache_miss Fired when subscribing to a cache channel that has no cached event available.

OnPusherMemberAdded / OnPusherMemberRemoved

These events use the new TsgcWSPusherMemberEvent callback type, which provides the channel name, user ID, and user info as separate parameters:

TsgcWSPusherMemberEvent = procedure(Sender: TObject;
  Channel, UserId, UserInfo: String) of object;
procedure TForm1.sgcPusherMemberAdded(Sender: TObject;
  Channel, UserId, UserInfo: String);
begin
  Memo1.Lines.Add('User joined: ' + UserId + ' on ' + Channel);
  if UserInfo  '' then
    Memo1.Lines.Add('  Info: ' + UserInfo);
end;

procedure TForm1.sgcPusherMemberRemoved(Sender: TObject;
  Channel, UserId, UserInfo: String);
begin
  Memo1.Lines.Add('User left: ' + UserId + ' from ' + Channel);
end;

OnPusherSubscriptionCount

Uses the new TsgcWSPusherSubscriptionCountEvent callback type. This event requires the Subscription Count feature to be enabled in your Pusher dashboard settings.

TsgcWSPusherSubscriptionCountEvent = procedure(Sender: TObject;
  Channel: String; SubscriptionCount: Integer) of object;
procedure TForm1.sgcPusherSubscriptionCount(Sender: TObject;
  Channel: String; SubscriptionCount: Integer);
begin
  Memo1.Lines.Add(Channel + ' now has ' + IntToStr(SubscriptionCount) +
    ' subscribers');
end;

OnPusherCacheMiss

Uses the new TsgcWSPusherCacheMissEvent callback type. This event is fired when you subscribe to a cache channel (cache-, private-cache-, private-encrypted-cache-, or presence-cache-) and no cached event is available.

TsgcWSPusherCacheMissEvent = procedure(Sender: TObject;
  Channel: String) of object;
procedure TForm1.sgcPusherCacheMiss(Sender: TObject; Channel: String);
begin
  Memo1.Lines.Add('No cached event for channel: ' + Channel);
end;

3. New REST API Methods

Two new server-side REST API methods have been added to the TsgcWS_API_Pusher component, covering the batch events endpoint and user connection termination.

Method Pusher Endpoint Description
TriggerBatchEvents POST /apps/{app_id}/batch_events Trigger up to 10 events in a single HTTP request. Each event can target a different channel.
TerminateUserConnections POST /apps/{app_id}/users/{user_id}/terminate_connections Terminate all WebSocket connections for a specific authenticated user.

TriggerBatchEvents

function TriggerBatchEvents(const aBatch: String): String;

The aBatch parameter is a JSON string containing a batch array of event objects. Each object must include channel, name, and data. Maximum 10 events per batch.

var
  vBatch, vResult: string;
begin
  vBatch :=
    '{"batch": [' +
    '  {"channel": "my-channel-1", "name": "my-event", "data": "{\"msg\":\"hello\"}"}' + ',' +
    '  {"channel": "my-channel-2", "name": "my-event", "data": "{\"msg\":\"world\"}"}' +
    ']}';

  vResult := sgcPusher.TriggerBatchEvents(vBatch);
  Memo1.Lines.Add(vResult);
end;

TerminateUserConnections

function TerminateUserConnections(const aUserId: String): String;

Forcefully terminates all active WebSocket connections for a given user. This is useful for logging out a user across all their devices, or revoking access after a permission change. Requires Pusher User Authentication to be configured.

var
  vResult: string;
begin
  // Disconnect user "user-123" from all sessions
  vResult := sgcPusher.TerminateUserConnections('user-123');
  Memo1.Lines.Add(vResult);
end;

4. Updated REST API Methods

Three existing REST API methods have been enhanced with additional optional parameters. Since the new parameters all have default values, existing code is fully backward-compatible.

TriggerEvent

Previous Signature

function TriggerEvent(const aEventName, aChannel, aData: String): String;

New Signature

function TriggerEvent(const aEventName, aChannel, aData: String;
  const aSocketId: String = '';
  const aInfo: String = ''): String;
Parameter Description
aSocketId Exclude this socket ID from receiving the event. Useful for preventing the sender from receiving their own broadcast.
aInfo Comma-separated list of attributes to return. Valid values: subscription_count, user_count.
var
  vResult: string;
begin
  // Trigger event, exclude the sender, and request subscription count
  vResult := sgcPusher.TriggerEvent('my-event', 'my-channel',
    '{"msg":"hello"}', sgcPusher.FSocket_id, 'subscription_count');
  Memo1.Lines.Add(vResult);
  // Response: {"channels":{"my-channel":{"subscription_count":5}}}
end;

GetChannels

Previous Signature

function GetChannels: String;

New Signature

function GetChannels(
  const aFilterByPrefix: String = '';
  const aInfo: String = ''): String;
Parameter Description
aFilterByPrefix Filter the returned channels by name prefix. Example: presence- returns only presence channels.
aInfo Comma-separated attributes. Valid values: user_count (presence only), subscription_count.
var
  vResult: string;
begin
  // List all presence channels with user counts
  vResult := sgcPusher.GetChannels('presence-', 'user_count');
  Memo1.Lines.Add(vResult);
  // Response: {"channels":{"presence-room":{"user_count":3}}}

  // List all channels (no filter) - backward-compatible call
  vResult := sgcPusher.GetChannels;
  Memo1.Lines.Add(vResult);
end;

GetChannel

Previous Signature

function GetChannel(const aChannel: String): String;

New Signature

function GetChannel(const aChannel: String;
  const aInfo: String = ''): String;
Parameter Description
aInfo Comma-separated attributes. Valid values: subscription_count, user_count, cache.
var
  vResult: string;
begin
  // Get channel info with subscription count and user count
  vResult := sgcPusher.GetChannel('presence-room',
    'subscription_count,user_count');
  Memo1.Lines.Add(vResult);
  // Response: {"occupied":true,"subscription_count":5,"user_count":3}

  // Get basic channel info - backward-compatible call
  vResult := sgcPusher.GetChannel('my-channel');
  Memo1.Lines.Add(vResult);
end;

5. Bug Fixes

Several bugs were identified and corrected in this release:

Bug Impact Fix
Typo in DoReadEvent: 'puserh:ping' Critical Server pings were never recognized, causing the client to miss heartbeat requests. This could lead to the server disconnecting the client for inactivity. Corrected to 'pusher:ping'.
DoSendPong null access High Accessing JSON.Node['data'].Value without null check could cause an access violation if the ping message has no data field. Added null check; defaults to {} when data is absent.
GetUsers hardcoded prefix High The method always prepended presence- to the channel name, doubling the prefix if the user already passed the full channel name (e.g., presence-presence-room). Removed hardcoded prefix. Users should now pass the full channel name including the presence- prefix.
GetUsers double semicolon Low A stray double semicolon ;; was present at the end of the method. Removed the extra semicolon.

Breaking change in GetUsers: The GetUsers method no longer prepends presence- automatically. If your code passes just the channel name without the prefix (e.g., GetUsers('my-room')), you must update it to include the full name: GetUsers('presence-my-room').


6. Summary

Category Count Details
New Channel Types 2 pscPrivateEncryptedChannel, pscPrivateEncryptedCacheChannel
New WebSocket Events 4 OnPusherMemberAdded, OnPusherMemberRemoved, OnPusherSubscriptionCount, OnPusherCacheMiss
New REST Methods 2 TriggerBatchEvents, TerminateUserConnections
Updated REST Methods 3 TriggerEvent (+socket_id, +info), GetChannels (+filter, +info), GetChannel (+info)
New Properties 1 SharedSecret on TsgcWSPusherResponseAuthentication
Bug Fixes 4 Ping typo, pong null access, GetUsers prefix, double semicolon

All changes are backward-compatible. New method parameters use default values and new event properties are optional. The only behavioral change is in GetUsers, which no longer prepends the presence- prefix automatically.

Stay Informed

When you subscribe to the blog, we will send you an e-mail when there are new updates on the site so you wouldn't miss them.

CEX Plus API Update sgcWebSockets

Related Posts