Skip to main content
Markdown

Python reference: wordplus-realtime

The package needs Python 3.9 or later, with PyNaCl. It takes the same options as the Node and PHP packages, and answers the browser the same way.

Realtime( site, secret, local_key, **options )​

Realtime.from_environment( **options ) is the same, with site, secret and local_key taken from WORDPLUS_SITE, WORDPLUS_SECRET and WORDPLUS_LOCAL_KEY.

Option
site, secretyour app's key and secret
local_key64 hex characters of your own (encryption)
profilelambda user: {"name": …, "avatar": …, "url": …} or None: the user's directory entry, and by default their presence info and call info
has_keyslambda user: False cuts a user off every key
generationsa GenerationStore, for rotate_channel() and rotate_user()
sealFalse sends everything plain
basewhere your routes are, /api/realtime by default, for config()
headersheaders the browser sends to your routes, such as a CSRF token, for config()
worker, roomswhere your app serves the two files, for config()
token_ttl, call_token_ttlseconds: 6 hours by default; a channels token lasts at most a day, a calls token at most 6 hours
server, apiWordPlus Cloud by default
http(method, url, headers, body) -> (status, body), your own client for publishing

A user is the id your app gives, an integer or a string, or None for nobody. In this version, calls and the directory take integer ids only.

Owners​

Method
channel(prefix, lambda user, channel: answer)private and presence channels, by the start of their names; answers False, True, or {"id": …, "info": …} (channels, presence)
calls(prefix, lambda user, call, with_user: answer)calls and rooms; answers False, True, or {"info", "publish", "subscribe", "admin", "hidden"} (calls)
kind(kind, lambda rest, user, label: bool, newest=None)keys of your own kind (encryption)

The longest prefix that fits decides. The callbacks are plain functions, not async. Each method returns the instance, so calls chain.

Routes​

Method
answer_request(method, content_type, action, body, user)→ (status, body): one route, auth, calls or keys, for a request's raw body
handle(action, body, user)→ (status, body), for a body already decoded
wordplus_realtime.django.urls(realtime, user=…)the routes in Django (Django)
wordplus_realtime.fastapi.router(realtime, user)the routes in FastAPI and Starlette (FastAPI)

The routes take JSON POSTs only, and answer anything else with 415. A refusal is part of the answer (denied), not an HTTP error.

MethodWhat it answers
authorize(channels, user){"token", "exp", "keys", "denied"}
authorize_call(request, user){"token", "exp", "key"} or {"denied": …}
keys_answer(labels, user){"keys", "identity"?}
identity(user){"token", "exp"} or None
token(channels, user=None, info=None, ttl=None)a channels token, signed as the auth route signs
config(user=None, headers=None)the browser's settings for connect(), with a signed-in user's keys and identity
may_join(channel, user), may_have(label, user)what the routes decide, for your own checks

Publishing​

Method
publish(channels, event, data=None, *, except_tab=None, seal=True)one channel, or up to 100; data up to 10 KB of JSON (publishing)
await apublish(…)the same from async code, on a worker thread
publish_batch([{"channels", "event", "data", "except", "seal"}, …])10 events a request
channel_info(channel){"subscriptions", "members"?}
rotate_channel(channel), rotate_user(user)moves a key on; needs generations

They raise RealtimeError when WordPlus Cloud refuses: e.code holds the code, and e.status the HTTP status.

Keys​

realtime.keys is the app's Keys: seal(text, label), seal_json(data, label), open(value), entry(label), day_label(), channel_label(channel), user_label(user).

Generations​

class GenerationStore(Protocol):
def get(self, name: str) -> Optional[int]: ...
def set(self, name: str, generation: int) -> None: ...

Keep them where all your servers see them, and for good: a database row, or a cache that doesn't evict.

Helpers​

channel_kind(channel) gives public, private or presence. valid_channel(channel) says whether WordPlus Cloud takes the name. label_of(value) names the key a sealed value needs. All three come from wordplus_realtime.