import type { AnyLanyardPayload, LanyardInitStatePayload, LanyardPayload, LanyardPresence, LanyardPresenceUpdatePayload } from "types/lanyard"; export class Lanyard { private socket: WebSocket; private readonly userId: string; private readonly updateCallback: (presence: LanyardPresence) => void; private heartbeatInterval?: number; constructor( userId: string, updateCallback: (presence: LanyardPresence) => void ) { this.userId = userId; this.updateCallback = updateCallback; this.socket = new WebSocket("wss://api.lanyard.rest/socket"); this.initialize(); } private send(data: AnyLanyardPayload) { this.socket.send(JSON.stringify(data)); } private initialize() { this.socket.onmessage = event => { const payload: LanyardPayload = JSON.parse(event.data); switch (payload.op) { case 1: { // Initialize this.send({ op: 2, d: { subscribe_to_ids: [this.userId] } }); this.send({ op: 3 }); const interval = "heartbeat_interval" in payload.d ? payload.d.heartbeat_interval : 30000; this.heartbeatInterval = window.setInterval(() => { this.send({ op: 3 }); }, interval); break; } case 0: { const typedPayload = payload as | LanyardInitStatePayload | LanyardPresenceUpdatePayload; const presence = typedPayload.t === "INIT_STATE" ? typedPayload.d[this.userId] : typedPayload.d; this.updateCallback(presence); break; } } }; } public close() { if (this.heartbeatInterval) { clearInterval(this.heartbeatInterval); } this.socket.close(); } }