Loading...
Searching...
No Matches
Janus TextRoom documentation

This is a plugin implementing a DataChannel only text room. As such, it does NOT support or negotiate audio or video, but only data channels, in order to provide text broadcasting features. The plugin allows users to join multiple text-only rooms via a single PeerConnection. Users can send messages either to a room in general (broadcasting), or to individual users (whispers). This plugin can be used within the context of any application that needs real-time text broadcasting (e.g., chatrooms, but not only).

The only message that is typically sent to the plugin through the Janus API is a `setup` message, by which the user initializes the PeerConnection itself, as explained in the Establishing a connection section. Apart from that, all other messages can be exchanged directly via Data Channels. For room management purposes, though, requests like `create` , `edit` , `destroy` , `list` , `listparticipants` , `exists` and `announcement` are available through the Janus API as well: notice that in this case you'll have to use `request` and not `textroom` as the name of the request. You cannot join a room or send/receive messages via Janus API: that's only possible through the datachannel interface.

Each room can also be configured with an HTTP backend to contact for incoming messages. If configured, messages addressed to that room will also be forwarded, by means of an HTTP POST, to the specified address. Notice that this will only work if libcurl was available when configuring and installing Janus.

Note
  This plugin is only meant to showcase what you can do with data channels involving multiple participants at the same time. While functional, it's not inherently better or faster than doing the same thing using the Janus API messaging itself (e.g., as part of the plugin API messaging) or using existing instant messaging protocols (e.g., Jabber). In fact, while data channels are being used, you're still going through a server, so it's not really peer-to-peer. That said, the plugin can be useful if you don't plan to use any other infrastructure than Janus, and yet you also want to have text-based communication (e.g., to add a chatroom to an audio or video conference).

Notice that, in general, all users can create rooms. If you want to limit this functionality, you can configure an admin `admin_key` in the plugin settings. When configured, only "create" requests that include the correct `admin_key` value in an "admin_key" property will succeed, and will be rejected otherwise.

Rooms to make available at startup are listed in the plugin configuration file. A pre-filled configuration file is provided in `conf/janus.plugin.textroom.cfg` and includes a demo room for testing.

To add more static rooms or modify the existing one, you can use the following syntax:

    [<unique room ID>]
    description = This is my awesome room
    is_private = true|false (whether this room should be in the public list, default=true)
    secret = <optional password needed for manipulating (e.g. destroying) the room>
    pin = <optional password needed for joining the room>
    history = <number of messages to store as a history, and send back to new participants (default=0, no history)>
    post = <optional backend to contact via HTTP post for all incoming messages>

As explained in the next sections, you can also create rooms programmatically.

# Establishing a connection

While some requests to the plugin can be sent via Janus API, the actual chatroom functionality is only available via datachannel. This means that you need to establish a WebRTC PeerConnection first, if you want to take full advantage of the functionality provided by the TextRoom plugin.

To do that, you use the `setup` request, which doesn't require any argument:

    {
            "request" : "setup"
    }

Assuming a datachannel wasn't established already, the plugin will react to that request with a basic event, which will contain a JSEP offer too:

    {
            "textroom" : "event",
            "result" : "ok"
    }

To complete the negotiation process and establish a WebRTC PeerConnection, you need to send an empty `ack` request with your JSEP answer:

    {
            "request" : "ack"
    }

Should you need to perform an ICE restart, e.g., to keep the datachannel alive during a connection migration, you can use an empty `restart` request:

    {
            "request" : "restart"
    }

Just as in the `setup` request, this will result in the plugin sending a new JSEP offer with an attempt to restart ICE. You can use the `ack` request to provide your JSEP answer in this case too.

Once the datachannel is active, you can start exchanging messages there, referring to the Text Room API for the syntax. As explained in the intro, some request described there can be sent over Janus API too, but you'll need to use `request` instead of `textroom` as the name of the request.

# Text Room API

All TextRoom API requests sent via datachannels are addressed by a `textroom` named property, and must contain a `transaction` string property as well, which will be returned in the response. Notice that, for the sake of brevity, the `transaction` property will not be displayed in the documentation, although, as explained, it MUST be present, and WILL be included in all responses (but not in the unsolicited events, like join/leave or incoming messages).

To get a list of the available rooms (excluded those configured or created as private rooms) you can make use of the `list` request, which has to be formatted as follows:

    {
            "textroom" : "list",
            "admin_key" : "<plugin administrator key; optional>"
    }

A successful request will produce a list of rooms in a `success` response:

    {
            "textroom" : "success",
            "list" : [              // Array of room objects
                    {       // Room #1
                            "room" : <unique numeric ID>,
                            "description" : "<Name of the room>",
                            "pin_required" : <true|false, depending on whether the room is PIN-protected>,
                            "num_participants" : <count of the participants>,
                            "history" : <size of history, if any>
                    },
                    // Other rooms
            ]
    }

To get a list of the participants in a specific room, instead, you can make use of the `listparticipants` request, which has to be formatted as follows:

    {
            "request" : "listparticipants",
            "room" : <unique numeric ID of the room>
    }

A successful request will produce a list of participants in a `participants` response:

    {
            "textroom" : "success",
            "room" : <unique numeric ID of the room>,
            "participants" : [              // Array of participant objects
                    {       // Participant #1
                            "username" : "<username of participant>",
                            "display" : "<display name of participant, if any>"
                    },
                    // Other participants
            ]
    }

To create new TextRoom rooms you can use the `create` request. The API room creation supports the same fields as creation via configuration files, which means the request must be formatted as follows:

    {
            "textroom" : "create",
            "room" : <unique numeric room ID to assign; optional, chosen by plugin if missing>,
            "admin_key" : "<plugin administrator key; mandatory if configured>",
            "description" : "<description of room; optional>",
            "secret" : "<secret to query/edit the room later; optional>",
            "pin" : "<PIN required for participants to join room; optional>",
            "is_private" : <true|false, whether the room should be listable; optional, true by default>,
            "history" : <number of messages to store as a history, and send back to new participants (default=0, no history)>,
            "post" : "<backend to contact via HTTP post for all incoming messages; optional>",
            "permanent" : <true|false, whether the mountpoint should be saved to configuration file or not; false by default>
    }

A successful creation procedure will result in a `success` response:

    {
            "textroom" : "success",
            "room" : <unique numeric ID>,
            "permanent" : <true if saved to config file, false if not>
    }

If you requested a permanent room but a `false` value is returned instead, good chances are that there are permission problems.

An error instead (and the same applies to all other requests, so this won't be repeated) would provide both an error code and a more verbose description of the cause of the issue:

    {
            "textroom" : "event",
            "error_code" : <numeric ID, check Macros below>,
            "error" : "<error description as a string>"
    }

Once a room has been created, you can still edit some (but not all) of its properties using the `edit` request. This allows you to modify the room description, secret, pin, whether it's private or not and the backend to forward incoming messages to: you won't be able to modify other more static properties, though, like the room ID for instance. If you're interested in changing the ACL, instead, check the `allowed` message. An `edit` request has to be formatted as follows:

    {
            "textroom" : "edit",
            "room" : <unique numeric ID of the room to edit; mandatory>,
            "secret" : "<room secret; mandatory if configured>",
            "new_description" : "<new pretty name of the room; optional>",
            "new_secret" : "<new password required to edit/destroy the room; optional>",
            "new_pin" : "<new password required to join the room; optional>",
            "new_is_private" : <true|false, whether the room should appear in a list request; optional>,
            "permanent" : <true|false, whether the room should be also removed from the config file; default=false>
    }

A successful edit procedure will result in a `success` response:

    {
            "textroom" : "edited",
            "room" : <unique numeric ID>,
            "permanent" : <true if changes were saved to config file, false if not>
    }

On the other hand, `destroy` can be used to destroy an existing text room, whether created dynamically or statically, and has to be formatted as follows:

    {
            "textroom" : "destroy",
            "room" : <unique numeric ID of the room to destroy; mandatory>,
            "secret" : "<room secret; mandatory if configured>",
            "permanent" : <true|false, whether the room should be also removed from the config file; default=false>
    }

A successful destruction procedure will result in a `destroyed` response:

    {
            "textroom" : "destroyed",
            "room" : <unique numeric ID>,
            "permanent" : <true if the room was removed from config file too, false if not>
    }

This will also result in a `destroyed` event being sent to all the participants in the room, which will look like this:

    {
            "textroom" : "destroyed",
            "room" : <unique numeric ID of the destroyed room>
    }

You can check whether a room exists using the `exists` request, which has to be formatted as follows:

    {
            "textroom" : "exists",
            "room" : <unique numeric ID of the room to check; mandatory>
    }

A successful request will result in a `success` response:

    {
            "textroom" : "success",
            "room" : <unique numeric ID>,
            "exists" : <true|false>
    }

You can configure whether to check tokens or add/remove people who can join a room using the `allowed` request, which has to be formatted as follows:

    {
            "textroom" : "allowed",
            "secret" : "<room secret; mandatory if configured>",
            "action" : "enable|disable|add|remove",
            "room" : <unique numeric ID of the room to update; mandatory>,
            "allowed" : [
                    // Array of strings (tokens users might pass in "join", only for add|remove)
            ]
    }

A successful request will result in a `success` response:

    {
            "textroom" : "success",
            "room" : <unique numeric ID>,
            "allowed" : [
                    // Updated, complete, list of allowed tokens (only for enable|add|remove)
            ]
    }

If you're the administrator of a room (that is, you created it and have access to the secret) you can kick participants using the `kick` request. Notice that this only kicks the user out of the room, but does not prevent them from re-joining: to ban them, you need to first remove them from the list of authorized users (see `allowed` request) and then `kick` them. The `kick` request has to be formatted as follows:

    {
            "textroom" : "kick",
            "secret" : "<room secret; mandatory if configured>",
            "room" : <unique numeric ID of the room; mandatory>,
            "username" : "<unique username of the participant to kick; mandatory>"
    }

A successful request will result in a `success` response:

    {
            "textroom" : "success",
    }

This will also result in a `kicked` event being sent to all the other participants in the room, which will look like this:

    {
            "textroom" : "kicked",
            "room" : <unique numeric ID of the room>,
            "username" : "<unique username of the kicked participant>"
    }

For what concerns room participation, you can join a room using the `join` request, send messages (public and private) using the `message` request, and leave a room with `leave` instead.

A `join` request must be formatted as follows:

    {
            "textroom" : "join",
            "room" : <unique numeric ID of the room to join>,
            "pin" : "<pin to join the room; mandatory if configured>",
            "username" : "<unique username to have in the room; mandatory>",
            "display" : "<display name to use in the room; optional>",
            "token" : "<invitation token, in case the room has an ACL; optional>",
            "history" : <true|false, whether to retrieve history messages when available (default=true)>
    }

A successful join will result in a `success` response, which will include a list of all the other participants currently in the room:

    {
            "textroom" : "success",
            "participants" : [
                    {
                            "username" : "<username of participant #1>",
                            "display" : "<display name of participant #1, if any>"
                    },
                    // Other participants
            ]
    }

As explained previously, there's no hardcoded limit in how many rooms you can join with the same participant and on the same PeerConnection.

Notice that a successful `join` request will also result in a `join` event being sent to all the other participants, so that they're notified about the new participant getting in the room:

    {
            "textroom" : "join",
            "room" : <room ID>,
            "username" : "<username of new participant>",
            "display" : "<display name of new participant, if any>"
    }

To leave a previously joined room, instead, the `leave` request can be used, which must be formatted like this:

    {
            "textroom" : "leave",
            "room" : <unique numeric ID of the room to leave>
    }

A successful leave will result in a `success` response:

    {
            "textroom" : "success"
    }

Notice that a successful `leave` request will also result in a `leave` event being sent to all the other participants, so that they're notified about the participant that just left the room:

    {
            "textroom" : "leave",
            "room" : <room ID>,
            "username" : "<username of gone participant>"
    }

Finally, the `message` request allows you to send public and private messages within the context of a room. It must be formatted like this:

    {
            "textroom" : "message",
            "room" : <unique numeric ID of the room this message will refer to>,
            "to" : "<username to send the message to; optional, only needed in case of private messages>",
            "tos" : "<array of usernames to send the message to; optional, only needed in case of private messages>",
            "text" : "<content of the message to send, as a string>",
            "ack" : <true|false, whether the sender wants an ack for the sent message(s); optional, true by default>
    }

A `message` with no `to` and no `tos` is considered a public message, and so will be sent to all the participants in the room. In case either `to` or `tos` is specified, instead, this is considered to be a whisper, that is a private message only meant for the specified recipients. Notice that `to` and `tos` are mutually exclusive, and you cannot specify both.

`text` must be a string, but apart from that there's no limit on what you can put in there. It could be, for instance, a serialized JSON string, or a stringified XML document, or whatever makes sense to the application.

A successful message delivery will result in a `success` response, but only if `ack` was `true` in the `message` request. This was done by design, to allow users to disable explicit acks for every outgoing message, especially in case of verbose communications. In case an ack is required, the response will look like this:

    {
            "textroom" : "success"
    }

Incoming messages will come either as `message` events. In particular, `message` will notify the user about an incoming public or privave message, that is either a message that was sent to the whole room, or to the user individually:

    {
            "textroom" : "message",
            "room" : <room ID the message was sent to>,
            "from" : "<username of participant who sent the public message>",
            "date" : "<date/time of when the message was sent>",
            "text" : "<content of the message>",
            "whisper" : <true|false, depending on whether it's a public or private message>
    }

In case the `whisper` attribute is `true` it means the user actually received a private message from another participant in the room.

Another way of injecting text into rooms is by means of announcements. Announcements are basically messages sent by the room itself, rather than individual users: as such, only users or applications managing the room can send these announcements, as the room secret will be required for the purpose. The `announcement` request implements this feature in the TextRoom plugin, and must be formatted like this:

    {
            "textroom" : "announcement",
            "room" : <unique numeric ID of the room this announcement will be sent to>,
            "secret" : "<room secret; mandatory if configured>",
            "text" : "<content of the announcement to send, as a string>"
    }

In case the `announcement` request is accepted, the response will look like this:

    {
            "textroom" : "success"
    }

Incoming announcements will be received by participants as `announcement` events. The syntax is pretty much identical to how `message` looks like, with the difference that no `from` attribute will be included as the announcement will be seen as coming from the room itself:

    {
            "textroom" : "announcement",
            "room" : <room ID the announcement was sent to>,
            "date" : "<date/time of when the announcement was sent>",
            "text" : "<content of the announcement>"
    }