# Channel API and limits

> The canonical reference for room.plugin channels, which exchange real-time data, covering functions, value formats, and save and sending limits.

A channel (`room.plugin`) is a pathway for exchanging data other than chat messages in real time, between people in the same chat room or between your server and a chat room. This page is the canonical reference for functions, value formats, and limits. For the overall flow, see [Sending and receiving real-time data](/en/doc/2.4); for sending from a server, see [Server REST API](/en/doc/3.6).

## room.plugin {#room-plugin}

| Name | Description |
| --- | --- |
| `room.plugin.add(channel)` | Returns a channel object. Calling it again with the same name returns **the same object** |
| `room.plugin.TO_ALL` | `0`. If the `type` of received data is this value, it was sent to everyone |
| `room.plugin.ONLY_ME` | `1`. If the `type` of received data is this value, it was sent to you only |

Choose channel names that do not collide within the chat room. To use saving (`save`), the name must start with a letter or digit, contain only letters, digits, `_`, and `-`, and be 64 characters or fewer.

```js
U.chat('*').on('after.create#dice', function (room) {
	if (room.id !== 'my-room') return;
	var ch = room.plugin.add('dice');
	ch.onReceived(function (room, data) {
		room.print(data.user.nick + ' rolled: ' + data.data[0]);
	});
	ch.addParser(function (args) {
		ch.send([Math.floor(Math.random() * 6) + 1]);
	});
});
```

## Channel object {#channel}

| Function | Arguments | Description |
| --- | --- | --- |
| `send(values)` | Value array | Sends to everyone in the chat room. You, the sender, receive it too |
| `sendTo(nick, values)` | Nickname, value array | Sends to one person only. You do not receive it unless you target your own nickname. If the target does not exist, nothing happens (no error) |
| `save(values)` | Value array | Saves the latest state on the server. Moderator or higher. See [Saving](#save) below |
| `clear()` | — | Clears this channel's saved value. Moderator or higher |
| `onReceived(fn)` | `fn(room, data)` | Adds a receive callback. See [Receiving](#receive) below |
| `addParser(fn)` | `fn(args)` | Adds a callback that handles `!channelname args` input. See [Command input handling](#parser) below |
| `destroy()` | — | Removes the channel from the list. Registered callbacks are discarded too |

- The functions have no return value. Calls made before connecting are not sent. If the server rejects a call, an `error` event fires (permission 402, value error 101, limit 428).
- Callbacks added with `onReceived` and `addParser` cannot be removed individually. Because `add` returns the same object, callbacks get mixed if your channel name collides with another script's. Use a unique name.

## Value formats {#values}

Values you send are an **array**, and only the formats below are passed through as-is in each slot.

| Value sent | Value received |
| --- | --- |
| String | String |
| Integer 0 or greater | Number |
| Negative number, decimal | Number |
| `true`, `false` | `true`, `false` |
| `undefined` | `undefined` (empty slot) |
| Object, array, `null`, function | **Dropped.** The following values shift forward by one slot |

For structured data, split it across slots or put it in a single string slot with `JSON.stringify`. Wrap `JSON.parse` of a received string in `try`.

```js
ch.send(['move', 3, 5]);                                  // split across slots
ch.send(['state', JSON.stringify({ x: 3, y: 5 })]);       // one string slot
```

## Receiving {#receive}

The `onReceived` callback receives `(room, data)`.

| Key | Description |
| --- | --- |
| `data.user` | The sender's [user object](/en/doc/3.2#user-object) |
| `data.type` | `0` (`TO_ALL`, `send`) or `1` (`ONLY_ME`, `sendTo`, targeted sends from the server, saved values) |
| `data.data` | Value array |

- **Sent by the server:** Data sent through the REST API and saved values received again on join have `data.user.auth` set to `4` and an empty nickname. Check whether data came from the server only with `data.user.auth === 4`.
- Saved values also arrive with `auth` 4, so `auth` 4 alone does not prove the REST API sent it. Only moderators or higher can write saved values.
- Anyone can send other data. Do not trust it based on the nickname alone; validate the format and range of the values.
- If `before.plugin` returns `false`, `onReceived` callbacks are not called ([Events](/en/doc/3.3)).

## Command input handling {#parser}

In the default skin, when someone types `!channelname arg1 arg2` in the chat input, that channel's `addParser` callbacks are called with `(['arg1', 'arg2'])`. Arguments are strings split on spaces. You can trigger the same handling from code with `room.action.plugin('channelname', ['arg1'])`.

| Case | Result |
| --- | --- |
| No channel with that name | `room.action.plugin()` returns `false`. The default skin sends the input as a normal message |
| `before.plugin` returns `false` | `false`. Callbacks are not called |
| A callback returns `false` | `false`. Later callbacks are not called, and the default skin sends the input as a normal message |
| Otherwise | `true`. The input is not sent as a message |

Parsers run only in your own browser. To let others know, call `send` inside the callback.

## Saving {#save}

`save(values)` keeps the channel's latest state on the server. People who join later receive the saved value when they join (`type` 1, `auth` 4). **It is not sent to people currently connected**, so call `send` as well if needed.

| Item | Limit |
| --- | --- |
| Who can write | Moderator or higher (otherwise error 402) |
| Saved value per channel | Free rooms 1KB, paid rooms 4KB |
| Saved values for the whole room | 60KB |
| Number of channels in a free room | Effectively one. Saving to another channel clears the saved values of existing channels |
| Value formats | Strings, numbers, booleans |

- The size of one channel is the number of UTF-8 bytes of the value array encoded as JSON. The 60KB total is the size of the entire `{ channel: value array }` object, including channel names, encoded as JSON. If a limit is exceeded or the channel name does not follow the rules, the request is rejected with error 101.
- In a free room, when the saved value is replaced by another channel's, the person who saved gets a `control` event (`type: 'pluginDataReplaced'`).
- Values saved with `save` live only in the chat server's memory. They may be lost when the chat room closes or the server restarts, and they are deleted when a paid room becomes free.
- If you save from your server with `"save": true` in the [Server REST API](/en/doc/3.6#events), the value is also kept on uchat servers, so it is read again the next time the chat room opens, even after the chat room closes or the server restarts. In this case it is also sent to people connected now, and the limits are the same as in the table above. Use this for values that must last (live status, scores, goal amounts, and so on).

## Sending limits {#limits}

| Limit | Details |
| --- | --- |
| Free room channel sending | The total number of recipients over the last 60 seconds cannot exceed 100. Exceeding it gives error 428 |
| Paid rooms | No channel sending limit |
| Packet count | When packets sent from the same IP per second (messages, commands, and channel sends combined) reach 10, you get error 425; going further disconnects you |

Recipients are counted like this.

| Send | Deducted |
| --- | --- |
| `send`, REST API send to everyone | Number of connected users at the time |
| `sendTo`, REST API targeted send | 1 |

- Deducted amounts come back after 60 seconds. For example, in a free room with 20 connected users, you can `send` up to 5 times per minute.
- `send` and `sendTo` from page scripts share the same limit with REST API sends.
- In a free room that has hit the limit, `save` is also rejected with error 428.

For state that changes often (positions, timers), do not `send` every time; batch updates, or send only when something changes.
