2025-01-18 13:21:38 +01:00
|
|
|
|
|
|
|
|
2025-01-26 22:48:58 +01:00
|
|
|
/*class Message {
|
2025-01-18 13:21:38 +01:00
|
|
|
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
|
|
|
|
}
|
2025-01-26 22:48:58 +01:00
|
|
|
}*/
|
2025-01-18 13:21:38 +01:00
|
|
|
|
|
|
|
|
|
|
|
class Messages {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Room {
|
|
|
|
name = null
|
|
|
|
messages = []
|
|
|
|
constructor(name){
|
|
|
|
this.name = name
|
|
|
|
}
|
|
|
|
setMessages(list){
|
2025-01-24 03:28:43 +01:00
|
|
|
|
2025-01-18 13:21:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-01-24 03:28:43 +01:00
|
|
|
class InlineAppElement extends HTMLElement {
|
|
|
|
|
|
|
|
constructor(){
|
2025-01-25 03:46:33 +01:00
|
|
|
// this.
|
2025-01-24 03:28:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
class Page {
|
|
|
|
elements = []
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2025-01-25 03:46:33 +01:00
|
|
|
class RESTClient {
|
2025-01-27 03:16:44 +01:00
|
|
|
debug = false
|
2025-01-25 03:46:33 +01:00
|
|
|
|
|
|
|
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()
|
|
|
|
|
2025-01-25 05:50:23 +01:00
|
|
|
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
|
|
|
|
me._socket = new WebSocket(me._url)
|
|
|
|
console.debug("Connecting..")
|
|
|
|
|
|
|
|
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
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2025-01-26 22:48:58 +01:00
|
|
|
class Socket extends EventHandler {
|
|
|
|
ws = null
|
|
|
|
isConnected = null
|
|
|
|
isConnecting = null
|
2025-01-26 22:54:29 +01:00
|
|
|
url = null
|
2025-01-26 22:48:58 +01:00
|
|
|
connectPromises = []
|
|
|
|
constructor() {
|
|
|
|
super()
|
2025-01-27 00:56:06 +01:00
|
|
|
this.url = window.location.hostname == 'localhost' ? 'ws://localhost:8081/rpc.ws' : 'wss://' + window.location.hostname +'/rpc.ws'
|
2025-01-26 22:48:58 +01:00
|
|
|
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(){
|
|
|
|
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)
|
2025-01-26 22:54:29 +01:00
|
|
|
|
|
|
|
const ws = new WebSocket(this.url)
|
2025-01-26 22:48:58 +01:00
|
|
|
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()
|
|
|
|
|
|
|
|
}
|
|
|
|
me.connectPromises.forEach(resolve=>{
|
|
|
|
resolve(me)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
onData(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.")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2025-01-25 05:50:23 +01:00
|
|
|
|
2025-01-26 22:48:58 +01:00
|
|
|
class App extends EventHandler {
|
2025-01-18 13:21:38 +01:00
|
|
|
rooms = []
|
2025-01-26 22:48:58 +01:00
|
|
|
rest = rest
|
|
|
|
ws = null
|
|
|
|
rpc = null
|
2025-01-18 13:21:38 +01:00
|
|
|
constructor() {
|
2025-01-26 22:48:58 +01:00
|
|
|
super()
|
2025-01-18 13:21:38 +01:00
|
|
|
this.rooms.push(new Room("General"))
|
2025-01-26 22:48:58 +01:00
|
|
|
this.ws = new Socket()
|
|
|
|
this.rpc = this.ws.client
|
|
|
|
const me = this
|
|
|
|
this.ws.addEventListener("channel-message", (data) => {
|
|
|
|
me.emit(data.channel_uid,data)
|
|
|
|
})
|
2025-01-18 13:21:38 +01:00
|
|
|
}
|
2025-01-28 17:11:30 +01:00
|
|
|
async benchMark(times,message) {
|
2025-01-26 22:48:58 +01:00
|
|
|
if(!times)
|
|
|
|
times = 100
|
2025-01-28 17:11:30 +01:00
|
|
|
if(!message)
|
|
|
|
message = "Benchmark Message"
|
2025-01-26 22:48:58 +01:00
|
|
|
let promises = []
|
|
|
|
const me = this
|
|
|
|
for(let i = 0; i < times; i++){
|
|
|
|
promises.push(this.rpc.getChannels().then(channels=>{
|
|
|
|
channels.forEach(channel=>{
|
2025-01-28 17:11:30 +01:00
|
|
|
me.rpc.sendMessage(channel.uid,`${message} ${i}`).then(data=>{
|
|
|
|
|
2025-01-26 22:48:58 +01:00
|
|
|
})
|
|
|
|
})
|
|
|
|
}))
|
|
|
|
|
|
|
|
}
|
2025-01-27 03:16:44 +01:00
|
|
|
//return await Promise.all(promises)
|
|
|
|
|
2025-01-25 03:46:33 +01:00
|
|
|
}
|
2025-01-18 13:21:38 +01:00
|
|
|
|
|
|
|
|
2025-01-26 22:48:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const app = new App()
|