Releases: signalwire/signalwire-js
@signalwire/js v3.27.0
Summary
This release brings significant enhancements and new features to the SignalWireClient, focusing on improved video handling, expanded API capabilities, and better user management:
-
Video Handling:
- New
buildVideoElement
function to easily create and manage video elements for calls and rooms. - Video streams now automatically adjust to occupy the full width of the specified container.
- New
-
Enhanced API Capabilities:
- Added methods for managing addresses and conversations, including fetching details, sending messages, and subscribing to updates.
- New functionality to handle user variables when dialing calls, enabling more personalized and detailed session information.
-
User Management:
- Introduced methods to get subscriber info and manage WebRTC endpoints' online/offline status.
-
Unified Notifications:
- Unified approach for handling incoming call notifications via WebSocket and Push Notification, simplifying the development process.
-
Improved Flexibility:
- Options to specify video/audio constraints while making calls, allowing for more customized call setups.
These updates collectively aim to provide a more flexible, powerful, and user-friendly experience for developers using the SignalWireClient.
Added
userVariables
param added when dialing a Fabric call
const client = await SignalWire({
host: ...
token: ...,
})
const call = await client.dial({
...params,
rootElement: document.getElementById('rootElement'),
userVariables: {
"name": "John Doe",
"email": "[email protected]",
//...
"fullBrowserVersion": "125.0.0.0",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
}
})
2. buildVideoElement
function that creates and optionally injects a video DOM element for a given Call
or Room
object
const call = await client.dial({
// ...
})
await call.start();
const { element, unsubscribe } = await buildVideoElement({
room: call,
})
const container = document.getElementById('container');
container.appendChild(element)
Or, to also implicitly inject the video DOM element into your chosen container:
const { element, unsubscribe } = await buildVideoElement({
room: call,
rootElement: document.getElementById('container'),
})
getAddress
method added to theaddress
namespace inSignalWireClient
which returns the details about a particular address id
client.address.getAddress({id: <address_id_to_fetch_details>})
getConversations
,getConversationMessages
,createConversationMessage
methods added to theconversation
namespace inSignalWireClient
const conversations = await client.conversation.getConversations()
const conversationMessages = await client.conversation.getConversationMessages({ addressId: '...' })
// Subscribe to updates
client.conversation.subscribeToUpdates((newConversation) => {
console.log('>> newConversation', newConversation)
})
SignalWireClient.getSubscriberInfo
method returns the info about the current subscriber
const subscriber = await client.getSubscriberInfo()
online
andoffline
methods added toSignalWireClient
to register/unregister the WebRTC endpoint
await client.online()
//or
await client.offline()
sendMessage
andgetMessages
methods added to theconversation
namespace
const result = await client.conversation.getConversations();
const convo = result.data.filter(c => c.id == <address_id>)[0];
convo.sendMessage({
text: 'hello world~',
})
await convo.getMessages()
- Ability to specify page size when querying for the
conversation
and theaddress
namespace
await client.conversation.getConversations({ pageSize: 10 });
await client.conversation.getMessages({ pageSize: 10 });
await client.conversation.getConversationMessages({ pageSize: 10 });
const addressData = await client.address.getAddresses({
type: 'room',
displayName: 'john doe',
pageSize: 10
})
Changed
- The video stream will occupy the full width of the
rootElement
container (breaking)
- Handling notifications of incoming calls via both WebSocket and Push Notification is now unified (breaking)
// register invitation callback for push notification only
client.online({pushNotification: __incomingCallNotification})
// register invitation callback for both push notification and websocket
client.online({all: __incomingCallNotification})
//accept call using the invite notification
function __incomingCallNotification(invite){
const call = await invite.accept(document.getElementById('rootElement'))
}
- Both
SignalWireClient.dial
andCallInvite.accept
methods now accept an optionalrootElement
parameter to specify where to put the video stream
invite.accept(document.getElementById('rootElement'))
const call = await client.dial({
// ...
rootElement: document.getElementById('rootElement')
});
member.joined
event now emitted for all members in acall.joined
event
- Users can now pass video/audio constraints while making a call.
const call = await client.dial({
to: "/public/some_resource",
video: false, // boolean | MediaTrackConstraints
audio: true, // boolean | MediaTrackConstraints
})
@signalwire/realtime-api v4.0.0
Release of @signalwire/realtime-api v4.0.0
This marks the release of SignalWires new Relay Realtime-api v4 SDK. This SDK strives to model the SDK after the concept of PUC (Programmable Unified Communications.) The v4 SDK will include all the namespaces that were originally included in the v3 SDK:
Voice
Messaging
Chat
Video
PubSub
Task
Setting Up a Single Client in v4
- In Relay v4, a single client instance provides access to all namespaces, simplifying the development process and reducing code complexity. This unified client architecture makes the system more maintainable and efficient, offering a streamlined approach to accessing different functionalities within the Relay ecosystem.
This shift in architecture reflects a more modern and developer-friendly approach, focusing on ease of integration and simplicity, which are key in today's fast-paced development environments.
Setting Up a Single Client in v4:
import { SignalWire } from "@signalwire/realtime-api";
const client = await SignalWire({ project: "ProjectID Here", token: "Token Here" });
// Access voice functionalities through the unified client
const voiceClient = client.voice;
const messagingClient = client.messaging;
Advanced Event Listening in Relay v4
Relay v4 introduces a new approach, offering more granular control over applications by allowing
listening to events not only on the Call
and RoomSession
but also on particular sessions through a new method/parameter called listen
.
Examples of some sessions that can be directly listened to are: Collects
, Recordings
, Playback
, Detects
, etc.
You can now listen to when this particular session has: Started
, Updated
, Failed
, Ended
, and some other session-specific events.
Example:
import { SignalWire } from "@signalwire/realtime-api";
const client = await SignalWire({ project: "ProjectID Here", token: "Token Here" })
const voiceClient = client.voice;
// Setup a Voice Client and listen for incoming calls
await voiceClient.listen({
topics: ["office"],
onCallReceived: async (call) => {
call.answer();
console.log("Call received", call.id);
// Start a call collect session
await call.collect({
digits: {
max: 4,
digitTimeout: 10,
terminators: "#"
},
partialResults: true,
sendStartOfInput: true,
listen: {
onStarted: () => {
console.log("Collect started");
},
onInputStarted: (collect) => {
console.log("Collect input started:", collect.result);
},
onUpdated: (collect) => {
console.log("Collect updated:", collect.result);
},
onFailed: (collect) => {
console.log("Collect failed:", collect.result);
},
onEnded: async (collect) => {
console.log("Collect ended:", collect.result);
// Play back the digits collected
await call.playTTS({ text: `You entered ${collect.digits}` });
call.hangup()
}
}
}).onStarted();
}
});
Code Examples
Voice
import { SignalWire } from "@signalwire/realtime-api";
const client = await SignalWire({ project: "ProjectID Here", token: "Token Here" })
await client.voice.listen({
topics: ["office"],
onCallReceived: (call) => {
call.answer();
call.playTTS({ text: "Hello world" });
}
});
Messaging
import { SignalWire } from "@signalwire/realtime-api";
const client = await SignalWire({ project: "ProjectID Here", token: "Token Here"})
let messageClient = client.messaging;
await messageClient.listen({
topics: ["office"],
async onMessageReceived(message) {
console.log("Received message", message);
const response = await messageClient.send({
from: message.to,
to: message.from,
body: "Hello World!"
});
console.log("Sent message", await response);
}
})
Chat
import { SignalWire } from "@signalwire/realtime-api";
const client = await SignalWire({ project: "ProjectID Here", token: "Token Here" })
const chatClient = client.chat;
await chatClient.listen({
channels: ["channel1"],
onMemberJoined: async (member) => {
let members = await chatClient.getMembers({
channel: member.channel
});
let chatMessage = `Hello ${member.id}!
There are ${members.members.length} members in this channel.
The members are: ${members.members.map(m => m.id).join(", ")}`
await chatClient.publish({
channel: member.channel,
content: chatMessage
})
}
});
Video
import { SignalWire } from "@signalwire/realtime-api";
const client = await SignalWire({ project: "ProjectID Here", token: "Token Here" })
const videoClient = client.video;
await videoClient.listen({
onRoomStarted: async (room) => {
console.log("Room started", room.displayName);
},
onRoomEnded: async (room) => {
console.log("Room ended", room.displayName);
},
});
PubSub
import { SignalWire} from "@signalwire/realtime-api";
const client = await SignalWire({ project: "ProjectID Here", token: "Token Here" })
const pubSubClient = client.pubSub;
await pubSubClient.listen({
channels: ["welcome"],
onMessageReceived: (message) => {
console.log("Received message:", message);
}
});
Task
import { SignalWire } from "@signalwire/realtime-api";
const client = await SignalWire({ project: "ProjectID Here", token: "Token Here"})
const taskClient = client.task
await taskClient.listen({
topics: ['office'],
onTaskReceived: (payload) => {
console.log('Received task', payload)
}
});
@signalwire/js v3.25.0
Added
- The Browser SDK for Video RoomSession objects now includes a new function,
sendDigits
, which enables users to transmit DTMF (Dual-Tone Multi-Frequency) tones within a RoomSession. This enhancement allows for the sending of specific tone signals, akin to those produced by telephone key presses, directly within the video session environment.
room.sendDigits('1')
- The
hand-raise API
is a new feature added to Video SDKs for both real-time and browser-based applications. It introduces a visual cue around a participant's video when they 'raise their hand', enhancing interaction in virtual meetings. This API lets participants indicate their wish to speak without interrupting the session. Key features include the ability for participants to raise or lower their hands, and for moderators to prioritize those who have raised their hands, thereby improving the management and structure of participation in online group sessions.
// `room` is an instance of a RoomSession
// 'member' is an instance of RoomSessionMember
// Raise hand (browser only)
await room.setRaisedHand()
// Lower hand (browser only)
await room.setRaisedHand({ raised: false })
// Raise hand for a specific member
await room.setRaisedHand({ memberId: '...' })
// Lower hand for a specific member
await room.setRaisedHand({ raised: false, memberId: '...' })
// Enable/disable handraise prioritization
await room.setPrioritizeHandraise(true)
// Raise or lower this member's hand.
await member.setRaisedHand()
// check if a member's hand is raised
console.log(member.handraised)
Fixes
- Updated dependencies
- Resolve
CallCollect.ended()
only whenstate
is NOTcollecting
andfinal
is eitherundefined
ortrue
, andresult.type
is a validEND_STATE
@signalwire/realtime-api v3.12
Added
- Support for
lock
andunlock
on RoomSessions. This will allow the locking of theRoomSession
which will prevent new members from being able to join. Additionally, room previews are stopped and replaced with a locked image. TheRoomSession
can be unlocked through the unlock method.
const roomSession = new SignalWire.Video.RoomSession({
token: "<YourRoomToken>",
rootElement: document.getElementById("myVideoElement"),
});
// Short example that locks the room when a member joins and unlocks it when they leave.
roomSession.on("member.joined", (e) => {
roomSession.lock();
});
roomSession.on("member.leave", (e) => {
roomSession.unlock();
});
- Support for
nodeId
. Optional parameter fordialSip
anddialPhone
that allows you to direct your calls through a specified node. This can help with efficient geolocation routing.
try {
const call = await client.dialSip({
from: "sip:[email protected]",
to: "sip:[email protected]",
timeout: 30,
nodeId: "NODE ID HERE"
});
} catch (e) {
console.log("Call not answered.");
}
Updated
- @singalwire/core dependency.
@signalwire/js v3.24.0
Added
- Support for
lock
andunlock
onRoomSessions
. This will allow the locking of theRoomSession
which will prevent new members from being able to join. Additionally, room previews are stopped and replaced with a locked image. TheRoomSession
can be unlocked through theunlock
method.
const roomSession = new SignalWire.Video.RoomSession({
token: "<YourRoomToken>",
rootElement: document.getElementById("myVideoElement"),
});
// Short example that locks the room when a member joins and unlocks it when they leave.
roomSession.on("member.joined", (e) => {
roomSession.lock();
});
roomSession.on("member.leave", (e) => {
roomSession.unlock();
});
Fixed
- Removed Video prefix from
member.updated
events to emit them properly.
Updated
- @singalwire/webrtc dependency
- @signalwire/core dependecy
@signalwire/[email protected]
Release of the SWAIG SDK
This SWAIG SDK will provide users the tools to build their own SWAIG server in Node.js to help with their AI Agent. Users will be able to define functions for the AI to utilize such as a transfer function, send SMS function, start streaming function, etc...
This SDK implementation simplifies the process of hosting a server for your SignalWire AI Agent to perform server-side processing.
Example
import { SWAIG } from '@signalwire/swaig'
const server = await SWAIG({
user: "<USER_NAME_HERE>",
password: "<PASSWORD_HERE>",
generateMetaDataToken: () => uuid()
})
await server.addFunction({
// user: "<USER_NAME_HERE>",
// password: "<PASSWORD_HERE>",
// token: "<META_DATA_TOKEN>", // to check
name: "get_weather",
purpose: "use when inquired about weather anywhere across the globe",
argument: {
properties: {
location: {
description: "the location to check the weather in",
type: "string"
}
},
type: "object"
},
}, (params) => { /* implementation */ });
await server.run()
The above SWAIG server will expose the routes:
- GET /
- GET /get_weather
When the route /get_weather
is requested from a SignalWire AI agent, it will perform the get_weather
function.
Getting Started
To get started with the SWAIG SDK, please refer to our NPM package here
@signalwire/realtime-api v3.11.0
This new release of version 3.11 of the Realtime SDK introduces improvements and new additions to Voice and Video.
Highlights
Additional Methods Added To CallRecording
- pause: Ability to pause an active recording on a call.
const recording = await call.recordingAudio({ direction: "both"});
...
await recording.pause();
- resume: Ability to resume a paused recording on a call.
const recording = await call.recordingAudio({ direction: "both"});
...
await recording.resume();
detectInterruptions
Addition
This allows the application to restart message delivery to the answering machine when speech activity is detected after the detector is in a READY
state.
// Start the detector
const detector = await call.amd({ detectInterruptions: true})
New Alias For Context: topic
Now context
or contexts
will have a valid alias of topic
or topics
.
import { Voice } from "@signalwire/realtime-api";
const client = new Voice.Client({
project: "<project-id>",
token: "<api-token>",
topics: ["office"],
});
Fixes
- Fix the new zoomed Video layouts to be appropriately centered.
- Improvements to Detector for AMD detections.
@signalwire/realtime-api v3.10.0
This small release of version 3.10 of the Realtime SDK introduces a new method for Voice calls and fixes a Voice bug.
Improvements
We have added the call.pass()
method to pass an incoming call to another RELAY consumer. 6a35f0a3
This will allow a consumer to decline incoming calls without ending the call and redirect the call to another RELAY client.
client.on("call.received", async (call) => {
await call.pass();
});
Fixes
- Fixed an undefined
setPayload
method on the CallTap instance.a7426731
@signalwire/js v3.23.0
Version 3.23 of the JavaScript SDK introduces the groundwork for SignalWire's upcoming UCaaS offering Call Fabric.
Improvements
- Initial changes to set up a global
SignalWire
client.65b0eea5
This will allow users to access Call Fabric to create calls outside of a namespace as they currently do with aVoice.Client
orVideo.Client
. - Added a user-defined refresh token function called
onRefreshToken
to update an access token.b44bd6fb
This is a method on theSignalWire
client mentioned above.
client = await SignalWire({
host: document.getElementById('host').value,
token: document.getElementById('token').value,
rootElement: document.getElementById('rootElement'),
onRefreshToken: async () => {
// Fetch the new token and update the client
const newToken = await fetch('/foo')
await client.updateToken(newToken)
},
})
When the access token is expiring, onRefreshToken
will be called to fetch and set a new token without interruption to the client's operation.
@signalwire/realtime-api v3.9.2
The latest version of the Realtime SDK, version 3.9.2, includes an improvement for RELAY.
Improvements
- We now support call state webhook callbacks with
callStateUrl
andcallStateEvents
parameters ondial
andconnect
Voice Call methods.4e1116b6
Call states you may specify with callStateEvents
include created
, ringing
, answered
, ending
, and ended
.
If callStateUrl
and callStateEvents
parameters are included when creating or connecting a call:
const call = await client.dialPhone({
to: '+123456',
from: '+19876',
timeout: 30,
callStateUrl: 'http://mydomain.com/hook',
callStateEvents: ['ended', 'answered']
})
the RELAY system will make a request to the specified webhook with the following payload:
"params": {
"event_type": "calling.call.state",
"event_channel": "signalwire_...",
"timestamp": 123457.1234,
"space_id": "<UUID>",
"project_id": "<UUID>",
"params": {
"node_id": "<UUID>",
"call_id": "<UUID>",
"tag": "<ID>",
"device": {
"type": "phone",
"params": {
"from_number": "+15551231234",
"to_number": "+15553214321"
}
},
"parent": {
"node_id": "<UUID>",
"call_id": "<UUID>",
"device_type": "phone"
},
"peer": {
"node_id": "<UUID>",
"call_id": "<UUID>"
},
"call_state": "ended",
"start_time": <time in ms>,
"answer_time": <time in ms>,
"end_time": <time in ms>,
"created_by": "dial"
}
}