Skip to content

Releases: signalwire/signalwire-js

@signalwire/js v3.27.0

06 Jun 11:59
fcd913c
Compare
Choose a tag to compare

Summary

This release brings significant enhancements and new features to the SignalWireClient, focusing on improved video handling, expanded API capabilities, and better user management:

  1. 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.
  2. 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.
  3. User Management:

    • Introduced methods to get subscriber info and manage WebRTC endpoints' online/offline status.
  4. Unified Notifications:

    • Unified approach for handling incoming call notifications via WebSocket and Push Notification, simplifying the development process.
  5. 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

  1. 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'), 
})

  1. getAddress method added to the address namespace in SignalWireClient which returns the details about a particular address id
client.address.getAddress({id: <address_id_to_fetch_details>})

  1. getConversations, getConversationMessages, createConversationMessage methods added to the conversation namespace in SignalWireClient
const conversations = await client.conversation.getConversations()

const conversationMessages = await client.conversation.getConversationMessages({ addressId: '...' })

// Subscribe to updates
client.conversation.subscribeToUpdates((newConversation) => {
  console.log('>> newConversation', newConversation)
})

  1. SignalWireClient.getSubscriberInfo method returns the info about the current subscriber
const subscriber = await client.getSubscriberInfo()

  1. online and offline methods added to SignalWireClient to register/unregister the WebRTC endpoint
await client.online()
//or
await client.offline()

  1. sendMessage and getMessages methods added to the conversation 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()

  1. Ability to specify page size when querying for the conversation and the address 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

  1. The video stream will occupy the full width of the rootElement container (breaking)

  1. 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'))
}

  1. Both SignalWireClient.dial and CallInvite.accept methods now accept an optional rootElement parameter to specify where to put the video stream
invite.accept(document.getElementById('rootElement'))

const call = await client.dial({
    // ...
      rootElement: document.getElementById('rootElement')
    });

  1. member.joined event now emitted for all members in a call.joined event

  1. 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

17 Apr 17:26
1ac1bd5
Compare
Choose a tag to compare

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

05 Dec 11:21
27b8538
Compare
Choose a tag to compare

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 when state is NOT collecting and final is either undefined or true, and result.type is a valid END_STATE

@signalwire/realtime-api v3.12

08 Nov 10:53
dfbe379
Compare
Choose a tag to compare

Added

  • Support for lock and unlock on RoomSessions. This will allow the locking of the RoomSession which will prevent new members from being able to join. Additionally, room previews are stopped and replaced with a locked image. The RoomSession 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 for dialSip and dialPhone 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

08 Nov 10:52
dfbe379
Compare
Choose a tag to compare

Added

  • Support for lock and unlock on RoomSessions. This will allow the locking of the RoomSession which will prevent new members from being able to join. Additionally, room previews are stopped and replaced with a locked image. The RoomSession 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();
});

Fixed

  • Removed Video prefix from member.updated events to emit them properly.

Updated

  • @singalwire/webrtc dependency
  • @signalwire/core dependecy

@signalwire/[email protected]

18 Sep 17:48
217bdfe
Compare
Choose a tag to compare

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

18 Sep 17:25
217bdfe
Compare
Choose a tag to compare

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

21 Jul 18:11
84d40b3
Compare
Choose a tag to compare

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

21 Jul 18:53
84d40b3
Compare
Choose a tag to compare

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 a Voice.Client or Video.Client.
  • Added a user-defined refresh token function called onRefreshToken to update an access token. b44bd6fb This is a method on the SignalWire 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

10 Jul 21:17
1a7dbb7
Compare
Choose a tag to compare

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 and callStateEvents parameters on dial and connect 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"
  }
}