Bit6 for JavaScript

GitHub version Bower version

Additional resources: GitHub Repo, Code Samples

Getting Started

Prerequisites

Add Bit6 SDK

Either use bower

bower install bit6

Or get the SDK from GitHub Repo. You just need bit6.min.js file.

Initialization

<script src="bit6.min.js"></script>
<script>
  var opts = {
    apikey: 'MyApiKey',
  };
  var b6 = new bit6.Client(opts);
</script>

Concepts

Event Emitters and Handlers

Multiple components in the Bit6 SDK act as event emitters.

The main Bit6 class bit6.Client emits the following events:

// Incoming call from another user
b6.on('incomingCall', function(d) {
  console.log('Incoming call', d);
});

// Got a real-time notification
b6.on('notification', function(n) {
  console.log('Got notification', n);
});

Data Changes

Bit6 synchronizes its data with the server. When any data object changes (for example a new message is received, a new user joins a group), the SDK emits a data change event.

// Listen to changes of 'message' data objects
b6.on('message', onMessageChange);

// o - data object
// op - operation performed: 1 - add, 0 - update, -1 - delete
function onMessageChange(o, op) {
  if (op > 0) {
    console.log('Message added', o);
  }
  else if (op < 0) {
    console.log('Message deleted', o);
  }
  else {
    console.log('Message updated', o);
  }
}

Authentication

To use calling and messaging capabilities of the SDK, the user needs to be authenticated.

Bit6 supports various authentication mechanisms described in the following sections. They are part of the Session class.

Each user can have one or more identities - user id, username, email, facebook id, google account, phone number etc. Identities are required for user authentication, managing contacts, identifying user's network. An identity is represented by a URI.

Check if the user is authenticated:

if (b6.session.authenticated) {
  console.log('User is logged in');
}
else {
  console.log('User is not logged in');
}

Logout:

b6.session.logout();

Managed

Username

A username is case-insensitive and must consist of alphanumeric characters, e.g. usr:john or usr:test123.

Create a new user account with a username identity and a password.

// Convert username to an identity URI
var ident = 'usr:' + 'john';
b6.session.signup({identity: ident, password: 'secret'}, function(err) {
  if (err) {
    console.log('signup error', err);
  }
  else {
    console.log('signup successful');
  }
});

Login into an existing account using an Identity and a password.

// Convert username to an identity URI
var ident = 'usr:' + 'john';
b6.session.login({identity: ident, password: 'secret'}, function(err) {
  if (err) {
    console.log('login error', err);
  }
  else {
    console.log('login successful');
  }
});

Anonymous

Bit6 also supports anonymous users. This is useful for situations where a user does not have an account, for example, if you want to have chatting with web visitors.

Bit6 app needs to be configured to support anonymous users. This is done with auth_anonymous app setting which can be set via a backend API or the developer portal. It specifies the TTL (time-to-live) value for the anonymous users. After user's TTL value expires, all the user data is deleted from the servers.

Sign in as an anonymous user:

b6.session.anonymous(function(err) {
  if (err) {
    console.log('error', err);
  }
  else {
    console.log('signed in');
  }
});

Third-Party

Bit6 integrates with various OAuth1 and OAuth2 providers for simplified user authentication.

Get configured providers

b6.session.getAuthInfo(function(err, infos) {
  if (err) {
    console.log('error', err);
  }
  else {
    console.log('configured auth providers:', infos);
  }
});

Sample response with OAuth2 configuration for Facebook and Google Account apps.

{
  "facebook": {
    "client_id": "1234567890"
  },
  "google": {
    "client_id": "abcxyz"
  }
}

Signin with an OAuth provider

Create a new Bit6 account or login into an existing one. In this example we use Facebook Login.

FB.login(function(resp) {
  if (resp.authRespone) {
    // resp.authRespone object contains an authorization code or access token
    b6.session.oauth('facebook', resp.authRespone, function(err) {
      if (err) {
        console.log('oauth error', err);
      }
      else {
        console.log('login done');
      }
    });
  }
  else {
    console.log('User cancelled login or did not fully authorize.');
  }
});

For live OAuth2 demo, visit videocalls.io.

Delegated

TODO: Add sample code

WebRTC Calling

When you start or receive a call, Bit6 provides you a controller object, bit6.Dialog, to controll the call.

Voice & Video

Start an Outgoing Call

// Start a call and get a controller (Dialog)
var d = b6.startCall('usr:john', {audio: true, video: true});
d.connect();

Handle an Incoming Call

b6.on('incomingCall', function(d) {
  console.log('Incoming call', d);
  // Decide if you want to accept or reject the call
  var acceptThisCall = true;
  // Accept this call and connect media
  if (acceptThisCall) {
    // You can specify what media you want to send
    var opts = {
      audio: true,
      video: false
    };
    // Start the call connection
    d.connect(opts);
  }
  // Reject this call
  else {
    d.hangup();
  }
});

End a call

// Hangup or reject a call
d.hangup()

Video elements

If you plan to send or receive video streams, you need to handle the placement of the video elements. SDK will notify you when a new local or remote video element will need to be displayed.

// Let's say you want to display the video elements in DOM element '#container'
// Get notified about video elements to be added or removed
// v - video element to add or remove
// d - Dialog - call controller. null for a local video feed
// op - operation. 1 - add, 0 - update, -1 - remove
b6.on('video', function(v, d, op) {
  var vc = $('#container');
  if (op < 0) {
      vc[0].removeChild(v);
  }
  else if (op > 0) {
      v.setAttribute('class', d ? 'remote' : 'local');
      vc.append(v);
  }
  // Total number of video elements (local and remote)
  var n = vc[0].children.length;
  // Display the container if we have any video elements
  if (op != 0) {
      vc.toggle(n > 0);
  }
});

Call events

You can notifications about the call progress by adding event handler to the instance of bit6.Dialog.

// Call progress
d.on('progress', function() {
  console.log('Call progress', d);
});
// Call answered
d.on('answer', function() {
  console.log('Call answered', d);
});
// Error during the call
d.on('error', function() {
  console.log('Call error', d);
});
// Call ended
d.on('end', function() {
  console.log('Call ended', d);
});

Renegotiation (beta!)

During the call you can add or remove streams at any time. For example, you can start a call as audio-only, and at a later stage add the camera video stream.

To make seamless changes to an existing call without disconnecting it, simply specify what media sources you want to add/remove.

// Stop sending camera video stream
d.connect({video: false});
// Start sending camera video stream
d.connect({video: true});

Phone/PSTN

Start a Phone Call

Bit6 interconnects with the phone networks (PSTN) and allows making outgoing phone calls.

Phone numbers must be in E164 format, prefixed with +. So a US (country code 1) number (555) 123-1234 must be presented as +15551231234.

Note, that for the demo purposes you can only make 1 minute free phone calls to the US and Canada numbers.

var d = b6.startPhoneCall('+15551231234');
d.connect();

P2P Data Transfers

Connection

The first step is to establish a connection between two users. The process is the same as a regular audio/video call. Moreover, you can enable data transfers with any audio/video call just by adding a data option.

A user initiates a connection to another user:

var d = b6.startCall('usr:john', {data: true});
d.connect();

The other user accepts the connection:

b6.on('incomingCall', function(d) {
  d.connect();
});

Send a transfer

This can be done from either user.

// Transfer's meta data
var info = {
  name: 'myfile.png',
  type: 'image/png',
  size: 10000
};
// Data can be an ArrayBuffer
var data = ...;
d.sendFile(info, data);

Receive a transfer

Information about new transfers or transfer progress is delivered via a transfer event.

d.on('transfer', function(tr) {
  console.log('Transfer', tr);
  if (tr.outgoing) {
    // Outgoing transfer
  }
  // Are we there yet?
  if (tr.completed()) {
    // Done
  }
  // Nope, still transferring
  else {
    console.log(tr.info.name + ' - ' + tr.percentage() + '%');
  }
});

IP Messaging

Messages

Message Class

A message object is an instance of bit6.Message class.

Send a Message

b6.compose('usr:john').text('Hello!').send(function(err) {
  if (err) {
    console.log('error', err);
  }
  else {
    console.log('message sent');
  }
});

Delete a Message

// m is an instance of a Message
b6.deleteMessage(m, function(err) {
  if (err) {
    console.log('error', err);
  }
  else {
    console.log('message deleted');
  }
});

Conversations

A conversation represents a message exchange with a remote address, which could be another user or a group.

Conversation Class

An instance of bit6.Conversation class. Contains a list of messages.

Messaging UI

Bit6 SDK automatically synchronizes the messages with the server and organizes them into conversations. It emits message and conversation data change events.

The app code should rely on these events to build and update the DOM elements in the UI.

This approach is implemented in the demo app included with the SDK. The demo displays a list of the chats and the messages of the currently selected chat. See a brief description below.

Conversations

The app code maintains a DOM representation for each conversation.

b6.on('conversation', function(c, op) {
  // We have two elements per conversation:
  // 1. Tab - shows conversation's view: title, unread etc
  // 2. Msgs - container for all the messages for this conversation
  var tabId = tabIdFromConvId(c.id);
  var msgsId = msgsIdFromConvId(c.id);
  // DOM elements
  var tab = $(tabId);
  var msgs = $(msgsId);
  // Deleted conversation
  if (op < 0) {
    // Remove DOM elements
    tab.remove();
    msgs.remove();
    return;
  }
  // New conversation
  if (op > 0) {
    // Create DOM elements
    tab = $('<div />').attr('id', tabId);
    msgs = $('<div />').attr('id', msgsId);
    // Add them into the main DOM tree
  }
  // Update 'tab' element with Conversation data
  tab.text(c.id + ' ' + c.unread + ' ' + c.updated);
});

Note that if there is a new message that needs a new conversation, the SDK will create a corresponding Conversation object and emit conversation event before emitting the message event.

Messages

Similarly the app maintains a DOM represenation for each message.

b6.on('message', function(m, op) {
  // Id for the DOM element showing this Message, based on m.id
  var divId = domIdFromMessageId(m.id);
  // DOM element
  var div = $(divId);
  // Deleted message
  if (op < 0) {
    // Remove DOM element
    div.remove();
    return;
  }
  // New message
  if (op > 0) {
    // Create the DOM element
    div = $('<div />').attr('id', divId);
    // Add it to the corresponding Conversation's
    // messages container
    var msgsId = msgsIdFromConvId( m.getConversationId() );
    $(msgsId).append(div);
  }
  // Update 'div' with the Message data
  div.text(m.content);
});

Attachments

Send a Photo, Video or Audio Message

You can easily send a rich media message by attaching a file to an outgoing message. The file handle can be obtained using:

Once you have a file object, sending a message is super simple. Note that the attached file can be uploaded directly to your AWS S3 bucket without touching Bit6 servers.

b6.compose('usr:john').text('Hello!').attach(file).send(function(err) {
  if (err) {
    console.log('error', err);
  }
  else {
    console.log('message sent');
  }
});

Processing a Message

A Message represents a text message, media message or a call history item.

Please check the sample code that shows how to handle various message types.

Groups

Management

Get a group:

var g = b6.getGroup(id);

Create a new group. The current user will become the group administrator:

// Group information - meta data, permissions
var opts = {
  meta: {
    title: 'Hello World'
  }
};
// Create the group
b6.createGroup(opts, function(err, g) {
  if (err) {
    console.log('error', err);
  }
  else {
    console.log('created group', g);
  }
});

Members

Join a group:

// Join group 'g1' with role 'user'
b6.joinGroup('g1', 'user', function(err, g) {
  if (err) {
    console.log('error', err);
  }
  else {
    console.log('joined group', g);
  }
});

Leave a group:

// Leave group 'g1'
b6.leaveGroup('g1', function(err) {
  if (err) {
    console.log('error', err);
  }
  else {
    console.log('left group');
  }
});