secutorpro's picture
websocket client
e8845c2 verified
Raw History Blame Contribute Delete
2.51 kB
import { getToken } from './api.js';
const HDR = 9; // type(1) + groupId(4) + memberId(4)
const AUDIO = 0x01;
export class DispatchSocket {
constructor(handlers) {
this.handlers = handlers;
this.closed = false;
this.retries = 0;
this.connect();
}
connect() {
const proto = location.protocol === 'https:' ? 'wss' : 'ws';
this.ws = new WebSocket(`${proto}://${location.host}/ws`);
this.ws.binaryType = 'arraybuffer';
this.ws.onopen = () => {
this.retries = 0;
this.ws.send(JSON.stringify({ type: 'auth', token: getToken() }));
this.handlers.onStatus?.(true);
};
this.ws.onmessage = (ev) => {
if (typeof ev.data === 'string') {
let msg;
try { msg = JSON.parse(ev.data); } catch { return; }
switch (msg.type) {
case 'welcome': this.handlers.onWelcome?.(msg); break;
case 'members': this.handlers.onMembers?.(msg.members); break;
case 'groups': this.handlers.onGroups?.(msg.groups); break;
case 'sos_new': this.handlers.onSosNew?.(msg.alert); break;
case 'sos_update': this.handlers.onSosUpdate?.(msg.alert); break;
case 'floor': this.handlers.onFloor?.(msg); break;
case 'error': console.warn('[ws]', msg.message); break;
}
} else if (ev.data instanceof ArrayBuffer && ev.data.byteLength > HDR) {
const dv = new DataView(ev.data);
if (dv.getUint8(0) !== AUDIO) return;
this.handlers.onAudio?.({
groupId: dv.getUint32(1),
memberId: dv.getUint32(5),
pcm: ev.data.slice(HDR),
});
}
};
this.ws.onclose = () => {
this.handlers.onStatus?.(false);
if (this.closed) return;
const delay = Math.min(10000, 500 * 2 ** this.retries++);
setTimeout(() => this.connect(), delay);
};
this.ws.onerror = () => this.ws.close();
}
send(obj) {
if (this.ws?.readyState === WebSocket.OPEN) this.ws.send(JSON.stringify(obj));
}
sendAudio(groupId, memberId, int16buffer) {
if (this.ws?.readyState !== WebSocket.OPEN) return;
const head = new Uint8Array(HDR);
const dv = new DataView(head.buffer);
dv.setUint8(0, AUDIO);
dv.setUint32(1, groupId);
dv.setUint32(5, memberId);
const body = new Uint8Array(int16buffer);
const out = new Uint8Array(HDR + body.byteLength);
out.set(head);
out.set(body, HDR);
this.ws.send(out);
}
close() {
this.closed = true;
this.ws?.close();
}
}