Events from the app
The app sends events to the rooms it is in, straight through WordPlus Cloud, with no request to your server: chat messages, typing, read ticks, reactions. A room rule says which events may be sent to it (events in depth):
- Node
- PHP
- Python
realtime.room( 'chat:{a}:{b}', { history: 50, emit: [ 'message', 'typing', 'reaction', 'read' ], webhook: [ 'message', 'reaction' ] },
( user, { a, b } ) => [ a, b ].includes( String( user ) ) );
$realtime->room( 'chat:{a}:{b}', fn ( $user, array $p ) => in_array( (string) $user, [ $p['a'], $p['b'] ], true ),
[ 'history' => 50, 'emit' => [ 'message', 'typing', 'reaction', 'read' ], 'webhook' => [ 'message', 'reaction' ] ] );
realtime.room("chat:{a}:{b}", lambda user, p: str(user) in (p["a"], p["b"]),
history=50, emit=["message", "typing", "reaction", "read"], webhook=["message", "reaction"])
Sending
val chat = rt.join("chat:34:42")
chat.emit("message", buildJsonObject { put("text", "Hi") }) // sent, nothing waited for
val ack = chat.emitWithAck("message", buildJsonObject { put("text", "Hi") }) // WordPlus Cloud took it: ack.id, ack.at
chat.volatile.emit("typing", buildJsonObject { put("on", true) }) // dropped rather than queued while off line, never kept
chat.emitWithAck("message", buildJsonObject { put("text", "Hi") }, timeoutMs = 5_000)
@Serializable data class Message(val text: String)
chat.emitWithAck("message", Json.encodeToJsonElement(Message("Hi"))) // your own types, through kotlinx.serialization
- Everyone in the room gets it, the other pages and devices of the sender too. This app does not.
emitWithAcksuspends until WordPlus Cloud has checked the event, kept it (when the room keeps history) and sent it on, and returns the kept eventidand time. It throws aRealtimeExceptionwith itscode:not_allowed(the rule does not list it),forbidden(not in the room),rate_limited,too_large,bad_event,timeout, orofflinewhile the connection is down.- Before the room is joined, an emit waits for it.
meta.fromon the other side is this app user, as your server vouched for them: an app cannot send as someone else.- Binary:
bytes.toJson()puts aByteArrayin the data, andelement.bytes()reads one back. - Limits: 1 MB an event (kept in history up to 64 KB), 50 a second a connection, in bursts of up to 100.
Hearing them
@Serializable data class ChatMessage(val text: String = "")
@Serializable data class Typing(val on: Boolean = false)
viewModelScope.launch { chat.eventsOf<ChatMessage>("message").collect { (message, meta) -> add(meta.from, message.text, meta.id) } }
viewModelScope.launch { chat.eventsOf<Typing>("typing").collect { (typing, _) -> showTyping(typing.on) } }
A message the app sent with emitWithAck does not come back to it: add it from the ack, with ack.id, so it is not shown twice when your server page or the room history has it too.
Keeping the conversation
The conversation is for your app to keep, on your server, as Better Messages keeps its own in the WordPress database. A room keeps its events for 30 minutes, for apps that reconnect. Your server gets the events the rule names in webhook and saves them by event_id, the id the app saw as meta.id, and a route of yours gives them back a page at a time, with the user token (keeping the conversation):
// What was said before: the conversation your server keeps, then what the room has that it may not have yet.
chat.on("joined") { _, _ ->
viewModelScope.launch {
val page = withContext(Dispatchers.IO) {
val request = Request.Builder().url("$server/chat/34/42/messages").header("Authorization", "Bearer $token").build() // ?before=<id> for the page before
http.newCall(request).execute().use { Json.parseToJsonElement(it.body!!.string()).jsonObject } // { messages: [ { id, from, text, at, reactions } ], more }
}
for (m in page["messages"]!!.jsonArray) add(m.jsonObject["from"]!!.jsonPrimitive.content, m.jsonObject["text"]!!.jsonPrimitive.content, m.jsonObject["id"]!!.jsonPrimitive.content)
for (e in chat.history(limit = 50).events) if (e.event == "message") add(e.meta.from, e.data.jsonObject["text"]!!.jsonPrimitive.content, e.meta.id)
}
}
- One id for each message, whichever way it came, so
addkeeps the first. - The events do not go through your server before they are sent on, so check there anything that matters.
Talking with the web chat
The Better Messages chat on your web pages (<wordplus-chat>) sends these events, so an app that sends the same reads with it, both ways:
| Event | Data | |
|---|---|---|
message | { text } | with an ack. Your server keeps it |
typing | { on } | volatile |
read | { id }, the newest message seen | volatile, for the sender ticks |
reaction | { id, key }, key empty to take it back | with an ack. Your server keeps it |