---
sidebar_position: 6
sidebar_label: Calls
---

# Calls in Flask

Calls between two people that ring in every tab and device the callee has open, group calls on the WordPlus Cloud media servers, and the Better Messages call screen: the buttons, the ring and the call window, with no call code. The media of a call is encrypted end to end, with keys your server gives out.

![Ben has pressed a video call button: his browser shows the call window calling Ann, over his own camera, and the browser of Ann rings, with buttons to answer and decline](/img/apps/calls-ringing.webp)

## Who may call

Your server declares calls as it declares rooms, by pattern:

```python
# app.py: calls between two people, support:34:42, the two it names, one of them staff.
def may_call(user, with_user, p):
    pair = [int(p["a"]), int(p["b"])]
    return user in pair and with_user in pair and (is_staff(user) or is_staff(with_user))

realtime.calls("support:{a}:{b}", may_call)

# The daily meeting: a group call for everyone signed in.
realtime.group_calls("meeting:{id}", lambda user: user is not None)
```

- **`with_user`** is the other person: whom `user` calls, or who calls `user`. Your server is asked again when the callee answers, with the callee as `user`.
- **The answer:** `False`, `True`, or a dict: `info` (what the others see of `user`: your `profile` by default), and in a group call `publish`, `subscribe`, `admin` and `hidden`.
- **The rooms of a group call** can be encrypted end to end too: `realtime.group_calls("meeting:{id}", rule, encrypt=True)`. The rooms that come from a call always are.
- **User ids are integers** for calls in this version. One that is not is refused with `no_user`.

The calls route (`/api/realtime/calls`) is already among your route answers.

## The call screen

Start it on every page where a call may ring, and put buttons where people call from:

```js
await rt.callScreen();   // the ring and the call window, on this page
```

```html
<wordplus-call to="{{ agent.id }}" call="support:{{ pair[0] }}:{{ pair[1] }}" type="video" name="{{ agent.name }}">Call {{ agent.name }}</wordplus-call>
<wordplus-room room="meeting:daily" type="video" title="Daily meeting">Join the meeting</wordplus-room>
```

- **A list with call buttons:** `<wordplus-online room="shop" call="support:{pair}">` puts one on the row of every member ([presence](presence.md#ready-made-who-is-online)).
- **Its words** come in English, German, Spanish, French, Italian, Dutch, Polish, Croatian and Hebrew, by the page `<html lang>`. `rt.callScreen( { strings: { … } } )` changes any of them.
- **Its look** is that of Better Messages, in a shadow root: your styles do not reach in, and its colours are CSS custom properties your page sets.
- **Group calls** need the room client your app serves (`rooms`), with its encryption worker beside it: `npx wordplus-realtime copy static/` puts both there.

![Ann has answered: each browser shows the other person camera, their own in a corner, and the call controls. The cameras are illustrations](/img/apps/calls-connected.webp)

## Adding people

Once a call is up, either side may ring someone else in, and it goes on as a group call. Say whom the call screen offers:

```python
# The staff not in the call, each still asked of may_call.
def staff_to_add(user, call):
    return [id for id in staff_ids() if str(id) not in call.split(":")[1:]]

realtime.calls("support:{a}:{b}", may_call, people=staff_to_add)
```

The screen then shows an Add person button, listing them with the names and pictures from your `profile`.

## Your own call interface

The call screen is built on this, and nothing else:

```js
const calls = await rt.calls();

const call = await calls.start( 'support:34:42', { to: 34, type: 'video' } );
call.on( 'state', ( state ) => show( state ) );   // ringing, connecting, connected, reconnecting, ended
call.on( 'remote', () => call.remote.camera?.attach( video ) );

calls.on( 'incoming', async ( incoming ) => {
    const answered = await incoming.accept();     // the call, or the group call it rings into. Or incoming.decline( 'busy' )
} );

const meeting = await calls.group( 'meeting:daily', { type: 'video' } );
meeting.on( 'person', ( person ) => person.camera?.attach( tileFor( person ) ) );
```

![Ann, Ben and Cara in one group call from three browsers, with the call screen grid: each sees the other two and their own camera, then the call controls. The cameras are illustrations](/img/apps/rooms-meeting.webp)

Every event, why a call ended, and what WordPlus Cloud sees: [calls in depth](../apps/calls.md).
