/*class Message { uid = null author = null avatar = null text = null time = null constructor(uid,avatar,author,text,time){ this.uid = uid this.avatar = avatar this.author = author this.text = text this.time = time } get links() { if(!this.text) return [] let result = [] for(let part in this.text.split(/[,; ]/)){ if(part.startsWith("http") || part.startsWith("www.") || part.indexOf(".com") || part.indexOf(".net") || part.indexOf(".io") || part.indexOf(".nl")){ result.push(part) } } return result } get mentions() { if(!this.text) return [] let result = [] for(let part in this.text.split(/[,; ]/)){ if(part.startsWith("@")){ result.push(part) } } return result } }*/ class Messages { } class Room { name = null messages = [] constructor(name) { this.name = name } setMessages(list) { } } class InlineAppElement extends HTMLElement { constructor() { // this. } } class Page { elements = [] } class RESTClient { debug = false async get(url, params) { params = params ? params : {} const encodedParams = new URLSearchParams(params); if (encodedParams) url += '?' + encodedParams const response = await fetch(url, { method: 'GET', headers: { 'Content-Type': 'application/json' } }); const result = await response.json() if (this.debug) { console.debug({ url: url, params: params, result: result }) } return result } async post(url, data) { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await response.json() if (this.debug) { console.debug({ url: url, params: params, result: result }) } return result } } const rest = new RESTClient() class EventHandler { constructor() { this.subscribers = {} } addEventListener(type, handler) { if (!this.subscribers[type]) this.subscribers[type] = [] this.subscribers[type].push(handler) } emit(type, ...data) { if (this.subscribers[type]) this.subscribers[type].forEach(handler => handler(...data)) } } class Chat extends EventHandler { constructor() { super() this._url = window.location.hostname == 'localhost' ? 'ws://localhost/chat.ws' : 'wss://' + window.location.hostname + '/chat.ws' this._socket = null this._wait_connect = null this._promises = {} } connect() { if (this._wait_connect) return this._wait_connect const me = this return new Promise(async (resolve, reject) => { me._wait_connect = resolve console.debug("Connecting..") try { me._socket = new WebSocket(me._url) }catch(e){ console.warning(e) setTimeout(()=>{ me.ensureConnection() },1000) } me._socket.onconnect = () => { me._connected() me._wait_socket(me) } }) } generateUniqueId() { return 'id-' + Math.random().toString(36).substr(2, 9); // Example: id-k5f9zq7 } call(method, ...args) { const me = this return new Promise(async (resolve, reject) => { try { const command = { method: method, args: args, message_id: me.generateUniqueId() } me._promises[command.message_id] = resolve await me._socket.send(JSON.stringify(command)) } catch (e) { reject(e) } }) } _connected() { const me = this this._socket.onmessage = (event) => { const message = JSON.parse(event.data) if (message.message_id && me._promises[message.message_id]) { me._promises[message.message_id](message) delete me._promises[message.message_id] } else { me.emit("message", me, message) } //const room = this.rooms.find(room=>room.name == message.room) //if(!room){ // this.rooms.push(new Room(message.room)) } this._socket.onclose = (event) => { me._wait_socket = null me._socket = null me.emit('close', me) } } async privmsg(room, text) { await rest.post("/api/privmsg", { room: room, text: text }) } } class Socket extends EventHandler { ws = null isConnected = null isConnecting = null url = null connectPromises = [] ensureTimer = null constructor() { super() this.url = window.location.hostname == 'localhost' ? 'ws://localhost:8081/rpc.ws' : 'wss://' + window.location.hostname + '/rpc.ws' this.ensureConnection() } _camelToSnake(str) { return str .replace(/([a-z])([A-Z])/g, '$1_$2') .toLowerCase(); } get client() { const me = this const proxy = new Proxy( {}, { get(target, prop) { return (...args) => { let functionName = me._camelToSnake(prop) return me.call(functionName, ...args); }; }, } ); return proxy } ensureConnection() { if(this.ensureTimer) return this.connect() const me = this this.ensureTimer = setInterval(()=>{ if (me.isConnecting) me.isConnecting = false me.connect() },5000) return this.connect() } generateUniqueId() { return 'id-' + Math.random().toString(36).substr(2, 9); } connect() { const me = this if (!this.isConnected && !this.isConnecting) { this.isConnecting = true } else if (this.isConnecting) { return new Promise((resolve, reject) => { me.connectPromises.push(resolve) }) } else if (this.isConnected) { return new Promise((resolve, reject) => { resolve(me) }) } return new Promise((resolve, reject) => { me.connectPromises.push(resolve) console.debug("Connecting..") const ws = new WebSocket(this.url) ws.onopen = (event) => { me.ws = ws me.isConnected = true me.isConnecting = false ws.onmessage = (event) => { me.onData(JSON.parse(event.data)) } ws.onclose = (event) => { me.onClose() } ws.onerror = (event)=>{ me.onClose() } me.connectPromises.forEach(resolve => { resolve(me) }) } }) } onData(data) { if(data.success != undefined && !data.success){ console.error(data) } if (data.callId) { this.emit(data.callId, data.data) } if (data.channel_uid) { this.emit(data.channel_uid, data.data) this.emit("channel-message", data) } } async sendJson(data) { return await this.connect().then((api) => { api.ws.send(JSON.stringify(data)) }) } async call(method, ...args) { const call = { callId: this.generateUniqueId(), method: method, args: args } const me = this return new Promise(async (resolve, reject) => { me.addEventListener(call.callId, (data) => { resolve(data) }) await me.sendJson(call) }) } onClose() { console.info("Connection lost. Reconnecting.") this.isConnected = false this.isConnecting = false this.ensureConnection().then(() => { console.info("Reconnected.") }) } } class App extends EventHandler { rooms = [] rest = rest ws = null rpc = null sounds = ["/audio/soundfx.d_beep3.mp3"] playSound(soundIndex) { if (!soundIndex) soundIndex = 0 const player = new Audio(this.sounds[soundIndex]); player.play() .then(() => { console.debug("Gave sound notification") }) .catch((error) => { console.error("Notification failed:", error); }); } constructor() { super() this.rooms.push(new Room("General")) this.ws = new Socket() this.rpc = this.ws.client const me = this this.ws.addEventListener("channel-message", (data) => { me.emit(data.channel_uid, data) }) } async benchMark(times, message) { if (!times) times = 100 if (!message) message = "Benchmark Message" let promises = [] const me = this for (let i = 0; i < times; i++) { promises.push(this.rpc.getChannels().then(channels => { channels.forEach(channel => { me.rpc.sendMessage(channel.uid, `${message} ${i}`).then(data => { }) }) })) } //return await Promise.all(promises) } } const app = new App()