|
|
|
|
|
/*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() |