WebSocket σε πραγματικό χρόνο

Συνδεθείτε στο wss://cliqer.io/ws για να ελέγχετε ένα δωμάτιο Cliqer από το δικό σας λογισμικό ή σύστημα show control, όπως με ένα απομακρυσμένο τηλεχειριστήριο παρουσιάσεων: αλλάζετε διαφάνειες, χειρίζεστε το χρονόμετρο του οικοδεσπότη, δείχνετε με μια κουκκίδα λέιζερ και στέλνετε μηνύματα στον οικοδεσπότη. Η εφαρμογή σας συμμετέχει στο δωμάτιο ως ομιλητής· το ίδιο το δωμάτιο φιλοξενείται πάντα από την εφαρμογή Cliqer για υπολογιστή.
session_token (τα προγράμματα περιήγησης το στέλνουν αυτόματα), μια παράμετρο ερωτήματος ?token= ή μια κεφαλίδα Authorization: Bearer. Το διακριτικό ελέγχεται κατά τη σύνδεση, όχι σε κάθε εντολή, οπότε όταν λήξει ανοίξτε νέα σύνδεση με νέο διακριτικό και μπείτε ξανά στο δωμάτιο. Χωρίς διακριτικό μπορείτε να συνδεθείτε, αλλά δεν μπορείτε να χρησιμοποιήσετε το API. Τα κλειδιά API ομάδας (cliqer_...) πιστοποιούν μόνο τα endpoints REST· σε αυτό το socket ένα κλειδί δεν αποτελεί διακριτικό συνεδρίας.Σύνδεση
# WebSocket connections require a WebSocket client
# Using websocat for testing:
websocat "$WS_URL/ws?token=YOUR_ACCESS_TOKEN"
const ws = new WebSocket('$WS_URL/ws?token=' + accessToken)
ws.onopen = () => {
console.log('Connected to Cliqer WebSocket')
}
ws.onmessage = (event) => {
const message = JSON.parse(event.data)
console.log('Received:', message)
}
ws.onerror = (error) => {
console.error('WebSocket error:', error)
}
import websocket
import json
ws = websocket.WebSocketApp(
f"$WS_URL/ws?token={access_token}",
on_message=lambda ws, msg: print(json.loads(msg)),
on_error=lambda ws, err: print(f"Error: {err}"),
on_open=lambda ws: print("Connected")
)
ws.run_forever()
import (
"github.com/gorilla/websocket"
)
url := "$WS_URL/ws?token=" + accessToken
conn, _, err := websocket.DefaultDialer.Dial(url, nil)
if err != nil {
log.Fatal("dial:", err)
}
defer conn.Close()
for {
_, message, err := conn.ReadMessage()
if err != nil {
break
}
fmt.Printf("Received: %s\n", message)
}
use WebSocket\Client;
$client = new Client("$WS_URL/ws?token={$accessToken}");
while (true) {
$message = $client->receive();
$data = json_decode($message, true);
print_r($data);
}
use tokio_tungstenite::{connect_async, tungstenite::Message};
use futures_util::{StreamExt, SinkExt};
let url = format!("$WS_URL/ws?token={}", access_token);
let (mut ws_stream, _) = connect_async(&url).await?;
while let Some(msg) = ws_stream.next().await {
match msg? {
Message::Text(text) => {
let data: serde_json::Value = serde_json::from_str(&text)?;
println!("Received: {:?}", data);
}
_ => {}
}
}
Ο διακομιστής στέλνει connected με το clientId σας μόλις ολοκληρώσει τη ρύθμιση της σύνδεσης. Περιμένετε αυτό το μήνυμα πριν στείλετε οτιδήποτε: ένα μήνυμα που φτάνει νωρίτερα απορρίπτεται με error και η σύνδεση κλείνει.
Σύνδεση & ροή δωματίου
Πλοήγηση διαφανειών
Ελέγξτε τις διαφάνειες ενός δωματίου από το δικό σας λογισμικό, όπως με ένα απομακρυσμένο τηλεχειριστήριο παρουσιάσεων. Η εφαρμογή σας συμμετέχει στο δωμάτιο ως ομιλητής, οπότε ο οικοδεσπότης τη βλέπει στη λίστα ομιλητών με το presenterName της και τη διαχειρίζεται όπως κάθε ομιλητή: ο χειριστής μπορεί να απενεργοποιήσει τα κλικ της, να την αφαιρέσει ή να την αποκλείσει.
- Συνδεθείτε με ένα διακριτικό συνεδρίας (
?token=, μια κεφαλίδαAuthorization: Bearerή το cookiesession_token). Τα κλειδιά API ομάδας λειτουργούν μόνο στα endpoints REST, όχι σε αυτό το socket. - Το πρόγραμμα του κατόχου του δωματίου πρέπει να περιλαμβάνει το Client API: το πρόσθετο API στα Presenter, Studio HD και Studio 4K, ενώ στα Studio 8K και Enterprise περιλαμβάνεται ήδη.
- Οι εντολές διαφανειών και χρονομέτρου εκτελούνται μόνο όσο ο οικοδεσπότης επιτρέπει κλικ από τη σύνδεσή σας.
Συμμετοχή στο δωμάτιο
{ "type": "joinRoom", "payload": { "room": "ABC123", "presenterName": "Show control", "api": true } }
- Στείλτε το μετά το συμβάν
connected: ο διακομιστής απορρίπτει οτιδήποτε φτάνει πριν από αυτό. - Το
presenterNameείναι το όνομα που βλέπει ο οικοδεσπότης (προεπιλογήAPI). Η εφαρμογή σας είναι πάντα ομιλητής στο δωμάτιο, ποτέ οικοδεσπότης του. - Περιμένετε ένα συμβάν
clicksEnabledμε payloadtrueπριν στείλετε εντολές. Ο οικοδεσπότης το στέλνει μόλις ρυθμίσει τη σύνδεσή σας και ξανά κάθε φορά που ο χειριστής ενεργοποιεί ή απενεργοποιεί τα κλικ σας. - Κρατήστε το
presenterKeyαπό τοjoinedRoomκαι στείλτε το στοjoinRoomόταν επανασυνδέεστε, ώστε ο οικοδεσπότης να διατηρήσει την άδεια κλικ σας. - Στέλνετε το πλαίσιο κειμένου
pingκάθε 15 δευτερόλεπτα για να παραμένει ανοιχτή η σύνδεση. Ο διακομιστής απαντά με το πλαίσιο κειμένουpong(όχι JSON).
Αποστολή εντολών
{ "type": "webAction", "payload": { "action": "goto", "slide": 5, "id": "a3" } }
action | Επιπλέον πεδίο | Τι κάνει |
|---|---|---|
next | Επόμενη διαφάνεια (ή κίνηση) | |
prev | Προηγούμενη διαφάνεια (ή κίνηση) | |
first | Πρώτη διαφάνεια | |
last | Τελευταία διαφάνεια | |
goto | slide: ακέραιος αριθμός από 1 έως 9999 | Μετάβαση σε αυτή τη διαφάνεια |
blackout | on: true ή false | Μαύρισμα της οθόνης ή επανεμφάνισή της |
- Το
idείναι προαιρετικό: 1 έως 64 γράμματα, ψηφία,_,.,:ή-. Επιστρέφεται στο αποτέλεσμα. Χρησιμοποιείτε νέοidγια κάθε εντολή: ο οικοδεσπότης απορρίπτει μια εντολή της οποίας τοidέχει ήδη λάβει από τη σύνδεσή σας τα τελευταία 5 δευτερόλεπτα, οπότε μια επανάληψη της ίδιας εντολής εκτελείται μία φορά. - Τα
first,last,gotoκαιblackoutεκτελούνται μόνο σε οικοδεσπότη του οποίου το πιο πρόσφατο συμβάνcontrolsτα αναφέρει στοnav. Ένας οικοδεσπότης που δεν στέλνει συμβάνcontrolsυποστηρίζει μόνοnextκαιprev. Με περισσότερους οικοδεσπότες στο δωμάτιο, κάθε οικοδεσπότης εκτελεί τις εντολές που υποστηρίζει. - Ένα
nextήprevενώ η οθόνη είναι μαύρη την εμφανίζει ξανά χωρίς μετακίνηση. - Ένα
gotoπέρα από την τελευταία διαφάνεια αγνοείται. - Στέλνετε το πολύ 10 εντολές ανά δευτερόλεπτο, μαζί οι εντολές διαφανειών και χρονομέτρου. Ο οικοδεσπότης απορρίπτει επίσης μια επανάληψη της ίδιας εντολής μέσα σε 50 χιλιοστά του δευτερολέπτου.
Τι υποστηρίζει σήμερα κάθε εφαρμογή παρουσιάσεων:
| Εφαρμογή παρουσιάσεων | next, prev | first, last | goto | blackout |
|---|---|---|---|---|
| PowerPoint σε Windows | Ναι | Ναι | Ναι | Ναι |
| PowerPoint σε Mac | Ναι | Ναι | Όχι | Όχι |
| Keynote | Ναι | Όχι | Όχι | Ναι |
| Google Slides (με συνδεδεμένη επέκταση προγράμματος περιήγησης) | Ναι | Ναι | Όχι | Όχι |
| vMix | Ναι | Όχι | Όχι | Όχι |
| Ενεργό παράθυρο | Ναι | Όχι | Όχι | Όχι |
Το συμβάν controls είναι η ζωντανή απάντηση για τον οικοδεσπότη που ελέγχετε: διαβάζετε το nav από αυτό και όχι από αυτόν τον πίνακα.
# One connection: wait for `connected`, join as an API client, give the host a
# moment to set it up, then send commands. Replies print as they arrive.
( sleep 2
echo '{"type":"joinRoom","payload":{"room":"ABC123","presenterName":"Show control","api":true}}'
sleep 3
echo '{"type":"webAction","payload":{"action":"next","id":"a1"}}'
sleep 1
echo '{"type":"webAction","payload":{"action":"prev","id":"a2"}}'
sleep 1 ) | websocat "$WS_URL/ws?token=YOUR_SESSION_TOKEN"
const ws = new WebSocket('$WS_URL/ws?token=' + accessToken)
const send = (message: object) => ws.send(JSON.stringify(message))
let ready = false
ws.onmessage = (event) => {
if (event.data === 'pong') return
const msg = JSON.parse(event.data)
if (msg.type === 'connected') {
send({ type: 'joinRoom', payload: { room: 'ABC123', presenterName: 'Show control', api: true } })
setInterval(() => ws.send('ping'), 15000) // keeps the connection open
}
if (msg.type === 'clicksEnabled' && msg.payload === true && !ready) {
ready = true
send({ type: 'webAction', payload: { action: 'next', id: 'a1' } })
// Also: { action: 'prev' }, { action: 'first' }, { action: 'last' },
// { action: 'goto', slide: 5 }, { action: 'blackout', on: true }
}
if (msg.type === 'controls') console.log('Commands this host supports:', ['next', 'prev', ...msg.payload.nav])
if (msg.type === 'webActionResult') console.log(`${msg.payload.action} delivered to ${msg.payload.hosts} host(s)`)
if (msg.type === 'slide') console.log('Slide:', msg.payload?.number ?? 'no deck open')
if (msg.type === 'error') console.error(msg.error, msg.payload?.message)
}
import json
import threading
import time
import websocket
ready = False
def send(ws, message):
ws.send(json.dumps(message))
def keepalive(ws): # keeps the connection open
while ws.sock and ws.sock.connected:
time.sleep(15)
ws.send("ping")
def on_message(ws, message):
global ready
if message == "pong":
return
msg = json.loads(message)
if msg["type"] == "connected":
send(ws, {"type": "joinRoom", "payload": {"room": "ABC123", "presenterName": "Show control", "api": True}})
threading.Thread(target=keepalive, args=(ws,), daemon=True).start()
elif msg["type"] == "clicksEnabled" and msg["payload"] is True and not ready:
ready = True
send(ws, {"type": "webAction", "payload": {"action": "next", "id": "a1"}})
# Also: {"action": "goto", "slide": 5}, {"action": "blackout", "on": True}, ...
elif msg["type"] == "webActionResult":
print(f"{msg['payload']['action']} delivered to {msg['payload']['hosts']} host(s)")
elif msg["type"] == "slide":
print("Slide:", msg["payload"]["number"] if msg["payload"] else "no deck open")
elif msg["type"] == "error":
print("Error:", msg["error"], msg["payload"]["message"])
ws = websocket.WebSocketApp(f"$WS_URL/ws?token={access_token}", on_message=on_message)
ws.run_forever()
import (
"encoding/json"
"fmt"
"log"
"sync"
"time"
"github.com/gorilla/websocket"
)
conn, _, err := websocket.DefaultDialer.Dial("$WS_URL/ws?token="+accessToken, nil)
if err != nil {
log.Fatal("dial:", err)
}
defer conn.Close()
var writeMu sync.Mutex // one writer at a time
send := func(message interface{}) {
writeMu.Lock()
defer writeMu.Unlock()
conn.WriteJSON(message)
}
go func() { // keeps the connection open
for range time.Tick(15 * time.Second) {
writeMu.Lock()
conn.WriteMessage(websocket.TextMessage, []byte("ping"))
writeMu.Unlock()
}
}()
ready := false
for {
_, data, err := conn.ReadMessage()
if err != nil {
break
}
if string(data) == "pong" {
continue
}
var msg struct {
Type string `json:"type"`
Error string `json:"error"`
Payload json.RawMessage `json:"payload"`
}
if json.Unmarshal(data, &msg) != nil {
continue
}
switch msg.Type {
case "connected":
send(map[string]interface{}{
"type": "joinRoom",
"payload": map[string]interface{}{"room": "ABC123", "presenterName": "Show control", "api": true},
})
case "clicksEnabled":
if string(msg.Payload) == "true" && !ready {
ready = true
send(map[string]interface{}{
"type": "webAction",
"payload": map[string]interface{}{"action": "next", "id": "a1"},
})
// Also: {"action": "goto", "slide": 5}, {"action": "blackout", "on": true}, ...
}
case "webActionResult", "slide", "error":
fmt.Printf("%s %s %s\n", msg.Type, msg.Error, msg.Payload)
}
}
use WebSocket\Client;
use WebSocket\TimeoutException;
// A 15 second read timeout doubles as the keepalive timer.
$client = new Client("$WS_URL/ws?token={$accessToken}", ['timeout' => 15]);
$ready = false;
while (true) {
try {
$message = $client->receive();
} catch (TimeoutException $e) {
$client->text('ping'); // keeps the connection open
continue;
}
if ($message === null) break; // closed
if ($message === 'pong') continue;
$msg = json_decode($message, true);
if ($msg['type'] === 'connected') {
$client->text(json_encode([
'type' => 'joinRoom',
'payload' => ['room' => 'ABC123', 'presenterName' => 'Show control', 'api' => true]
]));
} elseif ($msg['type'] === 'clicksEnabled' && $msg['payload'] === true && !$ready) {
$ready = true;
$client->text(json_encode([
'type' => 'webAction',
'payload' => ['action' => 'next', 'id' => 'a1']
]));
// Also: ['action' => 'goto', 'slide' => 5], ['action' => 'blackout', 'on' => true], ...
} elseif (in_array($msg['type'], ['webActionResult', 'slide', 'error'], true)) {
echo json_encode($msg), PHP_EOL;
}
}
use futures_util::{SinkExt, StreamExt};
use serde_json::{json, Value};
use tokio_tungstenite::{connect_async, tungstenite::Message};
let url = format!("$WS_URL/ws?token={}", access_token);
let (ws_stream, _) = connect_async(&url).await?;
let (mut write, mut read) = ws_stream.split();
let every = std::time::Duration::from_secs(15);
let mut keepalive = tokio::time::interval_at(tokio::time::Instant::now() + every, every);
let mut ready = false;
loop {
tokio::select! {
_ = keepalive.tick() => write.send(Message::Text("ping".into())).await?, // keeps the connection open
frame = read.next() => {
let Some(frame) = frame else { break };
let Message::Text(text) = frame? else { continue };
if text.as_str() == "pong" { continue }
let msg: Value = serde_json::from_str(&text)?;
match msg["type"].as_str() {
Some("connected") => {
let join = json!({"type": "joinRoom", "payload": {"room": "ABC123", "presenterName": "Show control", "api": true}});
write.send(Message::Text(join.to_string().into())).await?;
}
Some("clicksEnabled") if msg["payload"] == true && !ready => {
ready = true;
let next = json!({"type": "webAction", "payload": {"action": "next", "id": "a1"}});
write.send(Message::Text(next.to_string().into())).await?;
// Also: {"action": "goto", "slide": 5}, {"action": "blackout", "on": true}, ...
}
Some("webActionResult") | Some("slide") | Some("error") => println!("{}", msg),
_ => {}
}
}
}
}
Αποτελέσματα και συμβάντα
Κάθε εντολή λαμβάνει απάντηση με webActionResult ή error.
{ "type": "webActionResult", "payload": { "action": "next", "id": "a1", "hosts": 1 } }
Το hosts είναι ο αριθμός των οικοδεσποτών στους οποίους παραδόθηκε η εντολή. Η απόδειξη ότι εκτελέστηκε είναι το επόμενο συμβάν slide του οικοδεσπότη· το blackout αλλάζει επίσης το blackout στο επόμενο συμβάν controls του οικοδεσπότη. Μια εντολή που ο οικοδεσπότης δεν εκτελεί (απενεργοποιημένα κλικ, εντολή που δεν υποστηρίζει, goto πέρα από την τελευταία διαφάνεια) λαμβάνει αποτέλεσμα αλλά όχι συμβάν slide.
error | Πότε |
|---|---|
auth_required | joinRoom με api: true σε σύνδεση χωρίς διακριτικό συνεδρίας |
not_api_client | Εντολή από σύνδεση που δεν έχει μπει σε δωμάτιο με api: true ή αφού αποχώρησε |
invalid_web_action | Άγνωστο action ή slide, on ή id που λείπει ή δεν είναι έγκυρο |
invalid_timer_action | Άγνωστο action χρονομέτρου ή minutes ή id που λείπει ή δεν είναι έγκυρο |
invalid_message | Μήνυμα χωρίς text ή μεγαλύτερο από 1000 χαρακτήρες |
invalid_presenter_mouse | Θέση δείκτη εκτός του εύρους 0 έως 1 |
rate_limited | Περισσότερες από 10 εντολές διαφανειών και χρονομέτρου σε ένα δευτερόλεπτο ή περισσότερα από 5 μηνύματα σε 10 δευτερόλεπτα |
no_host | Κανένας οικοδεσπότης δεν είναι συνδεδεμένος στο δωμάτιο |
host_update_required | Όλοι οι οικοδεσπότες στο δωμάτιο εκτελούν παλαιότερη έκδοση του Cliqer που δεν δέχεται εντολές από το API |
api_not_in_plan | Το πρόγραμμα του κατόχου του δωματίου δεν περιλαμβάνει το Client API |
Τα σφάλματα περιέχουν ένα ευανάγνωστο message και, όταν ήταν σωστά διαμορφωμένα, το action και το id της εντολής:
{ "type": "error", "error": "invalid_web_action", "payload": { "message": "Unknown or incomplete webAction. Use next, prev, first, last, goto with slide, or blackout with on.", "action": "goto", "id": "a3" } }
Τα συμβάντα από έναν οικοδεσπότη φέρουν το id του στο hostId:
| Συμβάν | Payload |
|---|---|
clicksEnabled | true όταν ο οικοδεσπότης δέχεται τις εντολές σας, false όταν ο χειριστής απενεργοποίησε τα κλικ σας |
controls | { app, nav, blackout, timer }: η εφαρμογή παρουσιάσεων, οι επιπλέον εντολές διαφανειών που υποστηρίζει (first, last, goto, blackout), αν η οθόνη είναι μαύρη και αν ο οικοδεσπότης δέχεται εντολές χρονομέτρου |
slide | { number, current, total, title, notes } μετά από κάθε εντολή που εκτέλεσε ο οικοδεσπότης (το number είναι "4/12", το total είναι null όταν η εφαρμογή δεν το αναφέρει) ή null όταν ο οικοδεσπότης δεν έχει ανοιχτή παρουσίαση |
countdown | Ο χρόνος που δείχνει το χρονόμετρο του οικοδεσπότη, π.χ. "4:59" ("-0:12" σε υπέρβαση χρόνου), κάθε δευτερόλεπτο όσο τρέχει, ή null όταν σταματά |
countdownPhase | { phase, blink }: το phase είναι idle, talk, warning (απομένει λιγότερο από ένα λεπτό), overtime ή paused και το blink δείχνει αν το χρονόμετρο του οικοδεσπότη αναβοσβήνει |
messageFromHost | Απάντηση από τον χειριστή του οικοδεσπότη, δείτε Μηνύματα προς τον οικοδεσπότη |
Λαμβάνετε επίσης τα συμβάντα του ίδιου του δωματίου (clientJoined, clientLeft και άλλα)· αγνοήστε όσα δεν χρησιμοποιείτε. Αν ο χειριστής αφαιρέσει ή αποκλείσει τη σύνδεσή σας, λαμβάνετε logout με reason: "kicked".
Χρονόμετρο
Χειριστείτε το χρονόμετρο στην εφαρμογή Cliqer του οικοδεσπότη με τις ίδιες εντολές που στέλνει ένα Stream Deck ή το Bitfocus Companion.
{ "type": "timerAction", "payload": { "action": "set", "minutes": 25, "id": "t1" } }
action | Τι κάνει |
|---|---|
start | Έναρξη του χρονομέτρου ή συνέχιση μετά από stop |
stop | Παύση του χρονομέτρου |
reset | Διακοπή του χρονομέτρου και επιστροφή στον χρόνο στον οποίο είχε ρυθμιστεί |
plus1, plus2, plus5 | Προσθήκη 1, 2 ή 5 λεπτών |
minus1, minus2, minus5 | Αφαίρεση 1, 2 ή 5 λεπτών |
preset5, preset10, preset15, preset20 | Διακοπή του χρονομέτρου και ρύθμιση σε 5, 10, 15 ή 20 λεπτά |
set | Διακοπή του χρονομέτρου και ρύθμιση σε minutes, ακέραιο αριθμό από 1 έως 999 |
- Κάθε εντολή λαμβάνει απάντηση με
timerActionResult({ action, id, hosts }, με τοhostsόπως στις διαφάνειες) ήerror. Η απόδειξη ότι εκτελέστηκε είναι το επόμενο συμβάνcountdownήcountdownPhaseτου οικοδεσπότη. - Το χρονόμετρο λειτουργεί μόνο σε οικοδεσπότη του οποίου το πιο πρόσφατο συμβάν
controlsέχειtimer: true. Οι οικοδεσπότες με παλαιότερη έκδοση του Cliqer δεν το ανακοινώνουν και αγνοούν τις εντολές χρονομέτρου. - Οι εντολές χρονομέτρου ακολουθούν τους ίδιους κανόνες με τις εντολές διαφανειών: ο οικοδεσπότης τις εκτελεί μόνο όσο επιτρέπει κλικ από τη σύνδεσή σας και μοιράζονται το όριο των 10 εντολών ανά δευτερόλεπτο.
- Οι ρυθμίσεις χρονομέτρου του οικοδεσπότη ισχύουν ως έχουν: ένα χρονόμετρο ρυθμισμένο να μετρά προς τα πάνω μετρά προς τα πάνω, και ένα χρονόμετρο που ακολουθεί συνδεδεμένο χρονόμετρο υλικού παίρνει τον χρόνο του από εκείνη τη συσκευή.
Δείκτης λέιζερ
Εμφανίστε μια κουκκίδα λέιζερ στην κοινόχρηστη οθόνη του οικοδεσπότη, όπως κάνει το τηλέφωνο ενός ομιλητή.
{ "type": "presenterMouse", "payload": { "mouse": { "x": 0.5, "y": 0.25 } } }
- Τα
xκαιyείναι η θέση στην κοινόχρηστη οθόνη από την πάνω αριστερή γωνία της, από0έως1. - Στείλτε
{ "pressed": false }ως payload για να κρύψετε την κουκκίδα. Διαφορετικά εξαφανίζεται 30 έως 90 δευτερόλεπτα μετά την τελευταία θέση σας. - Η κουκκίδα εμφανίζεται μόνο όσο ο οικοδεσπότης μοιράζεται μια οθόνη και έχει ενεργοποιημένο τον Δείκτη λέιζερ. Το χρώμα της ακολουθεί το
presenterNameσας. - Στέλνετε το πολύ 30 θέσεις ανά δευτερόλεπτο. Οι επιπλέον θέσεις απορρίπτονται χωρίς σφάλμα και δεν υπάρχει συμβάν αποτελέσματος.
Μηνύματα προς τον οικοδεσπότη
Στείλτε ένα μήνυμα στον χειριστή του οικοδεσπότη. Εμφανίζεται στη συνομιλία ομιλητών του οικοδεσπότη, όπως ένα μήνυμα από το τηλέφωνο ενός ομιλητή, και οι απαντήσεις του χειριστή επιστρέφουν ως messageFromHost.
# Join as an API client, then send a message. Replies print as they arrive.
( sleep 2
echo '{"type":"joinRoom","payload":{"room":"ABC123","presenterName":"Show control","api":true}}'
sleep 3
echo '{"type":"messageToHosts","payload":{"text":"Two minutes to the break","id":"m1"}}'
sleep 30 ) | websocat "$WS_URL/ws?token=YOUR_SESSION_TOKEN"
// After joining (see Slide Navigation): send a message, print the replies.
send({ type: 'messageToHosts', payload: { text: 'Two minutes to the break', id: 'm1' } })
ws.addEventListener('message', (event) => {
if (event.data === 'pong') return
const msg = JSON.parse(event.data)
if (msg.type === 'messageToHostsResult') console.log(`Delivered to ${msg.payload.hosts} host(s)`)
if (msg.type === 'messageFromHost') console.log(`${msg.payload.fromPresenterName}: ${msg.payload.originalPayload.text}`)
})
# After joining (see Slide Navigation): send a message.
send(ws, {"type": "messageToHosts", "payload": {"text": "Two minutes to the break", "id": "m1"}})
# In on_message, print the replies:
# elif msg["type"] == "messageFromHost":
# print(f"{msg['payload']['fromPresenterName']}: {msg['payload']['originalPayload']['text']}")
// After joining (see Slide Navigation): send a message.
send(map[string]interface{}{
"type": "messageToHosts",
"payload": map[string]interface{}{"text": "Two minutes to the break", "id": "m1"},
})
// In the read loop, print the replies:
// case "messageFromHost":
// fmt.Printf("%s\n", msg.Payload)
// After joining (see Slide Navigation): send a message.
$client->text(json_encode([
'type' => 'messageToHosts',
'payload' => ['text' => 'Two minutes to the break', 'id' => 'm1']
]));
// In the read loop, print the replies:
// } elseif ($msg['type'] === 'messageFromHost') {
// echo $msg['payload']['originalPayload']['text'], PHP_EOL;
// After joining (see Slide Navigation): send a message.
let message = json!({"type": "messageToHosts", "payload": {"text": "Two minutes to the break", "id": "m1"}});
write.send(Message::Text(message.to_string().into())).await?;
// In the read loop, print the replies:
// Some("messageFromHost") => println!("{}", msg["payload"]["originalPayload"]["text"]),
Απάντηση από τον οικοδεσπότη:
{
"type": "messageFromHost",
"payload": {
"fromClientId": "host_abc123",
"fromPresenterName": "Stage PC",
"originalPayload": { "text": "Thanks, got it" }
},
"hostId": "host_abc123"
}
- Το
textέχει 1 έως 1000 χαρακτήρες. Στέλνετε το πολύ 5 μηνύματα κάθε 10 δευτερόλεπτα. - Κάθε μήνυμα λαμβάνει απάντηση με
messageToHostsResult({ id, hosts }) ήerror. - Ο χειριστής βλέπει το μήνυμα με το
presenterNameσας και ο οικοδεσπότης εμφανίζει ειδοποίηση όταν η ρύθμιση Ειδοποιήσεις είναι ενεργή.
Αναφορά συμβάντων
Εξερχόμενα συμβάντα (Πελάτης → Διακομιστής)
| Συμβάν | Περιγραφή |
|---|---|
joinRoom | Συμμετοχή σε δωμάτιο ως πελάτης API με api: true (δείτε Πλοήγηση διαφανειών) |
webAction | Έλεγχος διαφανειών (δείτε Πλοήγηση διαφανειών) |
timerAction | Έλεγχος χρονομέτρου (δείτε Χρονόμετρο) |
presenterMouse | Θέση δείκτη λέιζερ (δείτε Δείκτης λέιζερ) |
messageToHosts | Αποστολή μηνύματος στον χειριστή του οικοδεσπότη (δείτε Μηνύματα προς τον οικοδεσπότη) |
logout | Αποχώρηση από το δωμάτιο· η σύνδεση παραμένει ανοιχτή και μπορεί να μπει ξανά |
Εισερχόμενα συμβάντα (Διακομιστής → Πελάτης)
| Συμβάν | Περιγραφή |
|---|---|
connected | Αποστέλλεται κατά το άνοιγμα με { clientId, userId, isAuthenticated } |
joinedRoom | Επιτυχής συμμετοχή στο δωμάτιο |
clientJoined | Κάποιος μπήκε στο δωμάτιο |
clientLeft | Κάποιος αποχώρησε από το δωμάτιο |
webActionResult | Ένα webAction παραδόθηκε στους οικοδεσπότες του δωματίου |
timerActionResult | Ένα timerAction παραδόθηκε στους οικοδεσπότες του δωματίου |
messageToHostsResult | Ένα messageToHosts παραδόθηκε στους οικοδεσπότες του δωματίου |
clicksEnabled | Αν ένας οικοδεσπότης δέχεται τις εντολές σας |
controls | Τι υποστηρίζουν η εφαρμογή παρουσιάσεων και το χρονόμετρο ενός οικοδεσπότη |
slide | Η τρέχουσα διαφάνεια ενός οικοδεσπότη μετά από μια εντολή |
countdown | Ο χρόνος στο χρονόμετρο ενός οικοδεσπότη |
countdownPhase | Η φάση του χρονομέτρου ενός οικοδεσπότη |
messageFromHost | Μήνυμα από τον χειριστή του οικοδεσπότη |
logout | Ο χειριστής αφαίρεσε ή απέκλεισε τη σύνδεσή σας (reason: "kicked") |
error | Μήνυμα σφάλματος |