# Sending and receiving real-time data

> How to use channels (room.plugin) to exchange non-chat data between people in the same chat room, and how to validate the data you receive.

Channels let people in the same chat room exchange data other than chat messages in real time. Use them for features that keep everyone's screen in sync, such as watch-together, games, and polls. The authoritative list of functions and limits is [Channel API and limits](/en/doc/3.5).

## Open a channel {#open}

`room.plugin.add(channelName)` returns the channel object for that name. Calling it again with the same name returns the same object. People who use the same channel name exchange data with each other.

```js
U.chat('my-room').on('after.create', function (room) {
	var dice = room.plugin.add('dice');
});
```

Channel names must start with a letter or digit, contain only letters, digits, `_`, and `-`, and be 64 characters or fewer (`save` is rejected if the name breaks this rule). Prefix the name with your service name so it does not collide with other features.

## Send {#send}

Data is always sent as an **array**.

```js
dice.send([4]);                       // to everyone in the chat room (including you)
dice.sendTo('홍길동', ['hello', 1]);  // only to the one person with that nickname
```

- Array elements can only be **strings, numbers, and booleans**. Objects, arrays, and `null` are dropped in transit. If you need structure, split it across elements or send it as a string.
- The sender also receives data sent with `send`. With `sendTo`, only the recipient receives it. If the sender is under a hidden mute, both deliver only back to the sender.
- Free chat rooms have a limit on the total number of recipients over the last 60 seconds. Going over it causes error 428. For features that send often, reduce how often you send. → [Channel API and limits](/en/doc/3.5)

## Receive {#receive}

```js
dice.onReceived(function (room, msg) {
	// msg.user  the sender { nick, id, level, auth, ... }
	// msg.type  0 sent to everyone, 1 sent only to me
	// msg.data  the array that was sent
});
```

Like other events, the callback receives `(room, data)`. The same data also arrives through the `before.plugin` and `after.plugin` events (see [Events](/en/doc/3.3)).

## Validate received data {#validate}

Anyone in the chat room can use developer tools to send any value they want on a channel. Always validate on the receiving side.

- Check the number of elements, their types, and value ranges, and discard anything that does not match.
- When putting data on the screen, use `textContent` or escape it. `room.print` interprets its input as HTML.
- To check who sent the data, look at `msg.user.auth` (0 guest, 1 member, 2 moderator, 3 admin, 4 server). Do not decide based on the nickname alone.
- If a fair result matters (dice, draws), do not trust a value chosen by one person. Have your server decide it and send it through the server API. → [Sending notifications from your server to a chat room](/en/doc/2.5)

## Receive input commands {#parser}

If you register `addParser`, typing `!channelName args...` in the default skin's input box passes the argument array to that function. That input is not sent as a chat message. If the function returns `false`, the input is treated as unhandled and is sent as a normal message.

```js
dice.addParser(function (args) {
	// input: !dice  →  args: []
	dice.send([Math.floor(Math.random() * 6) + 1]);
});
```

## Save state {#save}

`save(array)` stores the channel's latest state on the server. People who join later receive the saved value when they enter. `clear()` deletes the saved value.

- Only moderators and above can save.
- Saving does not send anything to people who are already in the room. To notify them too, also call `send`.
- The saved value received on entry arrives with `type` 1 (only to me), and the sender is the server user (`auth` 4, no nickname). You cannot tell who saved it.
- There is a size limit on saved data, and in free chat rooms saving to a different channel deletes the saved value of the previous channel. → [Channel API and limits](/en/doc/3.5)

```js
// Save the video ID an admin picked, and also send it to people already here
video.save(['dQw4w9WgXcQ']);
video.send(['dQw4w9WgXcQ']);
```

## Close {#destroy}

`destroy()` closes the channel and discards the receive and command functions you registered. Calling `add` again with the same name opens a new channel.

## Full code {#full-code}

This example rolls a die and tells everyone when you type `!dice`.

```html
<script>U=window.U=window.U||{},U.events=U.events||[],U.chat=function(n){return{on:function(e,t){U.events.push([n,e,t])},off:function(e){for(var t=U.events.length;t>0;)U.events[--t][0]==n&&U.events[t][1]==e&&U.events.splice(t,1)}}};</script>
<script async src="https://client.uchat.io/uchat.js"></script>
<u-chat room="my-room" style="display:inline-block; width:100%; max-width:500px; height:500px;"></u-chat>
<script>
function escapeHtml(text) {
	return String(text).replace(/[&<>"']/g, function (c) {
		return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
	});
}

U.chat('my-room').on('after.create', function (room) {
	var dice = room.plugin.add('dice');

	dice.onReceived(function (room, msg) {
		var value = msg.data[0];
		if (msg.data.length !== 1 || typeof value !== 'number') return;
		if (value % 1 !== 0 || value < 1 || value > 6) return;
		var nick = msg.user ? msg.user.nick : '?';
		room.print(escapeHtml(nick) + ' rolled: ' + value);
	});

	dice.addParser(function () {
		dice.send([Math.floor(Math.random() * 6) + 1]);
	});
});
</script>
```

Larger examples such as watch-together and games are in [Watch YouTube together](/en/doc/4.1) and [Rock paper scissors](/en/doc/4.2).
