import { Injectable } from '@angular/core'; import { v4 as uuidv4 } from 'uuid' import { wsCallbacksParams, msgQueue, send } from 'src/app/models/rochet-chat-cliente-service' import { deepFind } from 'src/plugin/deep' import { environment } from 'src/environments/environment'; import { SessionStore } from 'src/app/store/session.service'; import { chatHistory, Rooms } from 'src/app/models/chatMethod'; @Injectable({ providedIn: 'root' }) export class WsChatService { isLogin = false; loginResponse = {} constructor() { } connect() { // dont connect if is already connected if(this.ws.connected == true) { return false } this.ws.connect(); const message = { msg: "connect", version: "1", support: ["1"] } this.ws.send({message, loginRequired: false, requestId: 'connectMessage'}) this.ws.send({message:{msg:"pong"}, loginRequired: false, requestId: 'connectPong'}) this.ws.registerCallback({ type:'Onmessage', key:'ping-pong', funx:(message: any) => { if(message.msg == "ping") { this.ws.send({message:{msg:"pong"}, loginRequired: false}) } } }) } login() { // dont login if is already login if(this.isLogin == true) { return new Promise((resolve, reject)=>{ resolve(this.loginResponse) }) } const requestId = uuidv4() // const message = { msg: "method", method: "login", id: requestId, params: [ { user: { email: SessionStore.user.Email }, password: SessionStore.user.Password } ] } this.ws.send({message, requestId: 'login', loginRequired: false}) return new Promise((resolve, reject) => { // this.ws.registerCallback({type:'Onmessage', funx:(message)=>{ // if(message.id == requestId ) { // same request send // // if(message.result) { if(message.result.token) { this.isLogin = true this.loginResponse = message setTimeout(()=>{ this.ws.wsMsgQueue() }, 10) resolve(message) // } } else { this.isLogin = false // reject(message) } reject(message) return true } }}) }); } getRooms(roomOlder = 1480377601) { //const requestId = uuidv4() const requestId = uuidv4() const message = { "msg": "method", "method": "rooms/get", "id": requestId, "params": [ { "$date": 1480377601 } ] } this.ws.send({message, requestId}) return new Promise((resolve, reject) => { this.ws.registerCallback({type:'Onmessage', funx:(message)=>{ if(message.id == requestId) { // same request send resolve(message) return true } }}) }); } readMessage(roomId) { //const requestId = uuidv4() const requestId = uuidv4() const message = { "msg":"method", "method":"readMessages", "params": [roomId, []], "id": requestId } this.ws.send({message, requestId}) return new Promise((resolve, reject) => { this.ws.registerCallback({type:'Onmessage', funx:(message)=>{ if(message.id == requestId) { // same request send resolve(message) return true } }}) }); } getUserOfRoom(roomId) { //const requestId = uuidv4() const requestId = uuidv4() const message = { "msg": "method", "method": "getUsersOfRoom", "id": requestId, "params": [ roomId, false, { "limit": 100, "skip": 0 } ] } this.ws.send({message, requestId}) return new Promise((resolve, reject) => { this.ws.registerCallback({type:'Onmessage', funx:(message)=>{ if(message.id == requestId) { // same request send resolve(message) return true } }}) }); } logout() { this.isLogin = false this.ws.connected = false this.ws.disconnect() } // send message to room send({roomId, msg, attachments = null, file = null, localReference = null}) { //const requestId = uuidv4() const requestId = uuidv4() var message = { msg: "method", method: "sendMessage", id: requestId, params: [{ rid: roomId, localReference: localReference, msg: msg, attachments, file }] } // this.ws.send({message, requestId}); return new Promise((resolve, reject) => { this.ws.registerCallback({type:'Onmessage', funx:(message)=>{ if(message.id == requestId ) { // same request send resolve({message, requestId}) return true } }}) }); } updateMessage(messageObject) { //const requestId = uuidv4() const requestId = uuidv4() var message = { msg: "method", method: "updateMessage", id: requestId, params: [messageObject] } this.ws.send({message, requestId}); return new Promise((resolve, reject) => { this.ws.registerCallback({type:'Onmessage', funx:(message)=>{ if(message.id == requestId ) { // same request send resolve({message, requestId}) return true } }}) }) } leaveRoom(roomId) { const requestId = uuidv4() var message = { msg: "method", method: "leaveRoom", id: requestId, params: [ roomId, ] } this.ws.send({message, requestId}); return new Promise((resolve, reject) => { this.ws.registerCallback({type:'Onmessage', funx:(message)=>{ if(message.id == requestId ) { // same request send resolve(message) return true } }}) }); } addRoomOwner(roomId, userId) { const requestId = uuidv4() var message = { msg: "method", method: "addRoomOwner", id: requestId, params: [ roomId, userId ] } this.ws.send({message, requestId}); return new Promise((resolve, reject) => { this.ws.registerCallback({type:'Onmessage', funx:(message)=>{ if(message.id == requestId ) { // same request send resolve(message) return true } }}) }); } hidingRoom(roomId) { const requestId = uuidv4() var message = { msg: "method", method: "hideRoom", id: requestId, params: [roomId] } this.ws.send({message, requestId}); return new Promise((resolve, reject) => { this.ws.registerCallback({type:'Onmessage', funx:(message)=>{ if(message.id == requestId ) { // same request send resolve(message) // return true } }}) }); } joinRoom() {} deleteMessage(msgId) { //const requestId = uuidv4(); const requestId = uuidv4() var message = { msg: "method", method: "deleteMessage", id: requestId, params:[{"_id":msgId}] } this.ws.send({message, requestId}); return new Promise ((resolve, reject) => { this.ws.registerCallback({type:'Onmessage', funx:(message) =>{ if(message.id == requestId ){ resolve(message) return true } }}) }); } createPrivateRoom(groupName, username, customFields) { const requestId = uuidv4() var message = { msg: "method", method: "createPrivateGroup", id: requestId, params: [ groupName, [username], false, customFields, { "broadcast":false, "encrypted":false } ] } this.ws.send({message, requestId}); return new Promise((resolve, reject) => { this.ws.registerCallback({type:'Onmessage', funx:(message)=>{ if(message.id == requestId ) { // same request send resolve(message) return true } }}) }); } sendStreamNotifyRoom(roomId : string, username, event: 'typing', param: any) { const requestId = uuidv4() let message = { msg: "method", method: "stream-notify-room", id: requestId, params: [ `${roomId}/${event}`, username, param ] }; this.ws.send({message, requestId}) return new Promise((resolve, reject) => { this.ws.registerCallback({type:'Onmessage', funx:(message)=>{ if(message.id == requestId ) { // same request send resolve(message) return true } }}) }); } subStreamNotifyRoom(roomId : string , event: 'typing' | 'deleteMessage' | 'readMessage', param: any) { const requestId = uuidv4() let message = { msg: "sub", id: requestId, name: "stream-notify-room", params:[ `${roomId}/${event}`, param ] } this.ws.send({message, requestId}) return new Promise((resolve, reject) => { this.ws.registerCallback({type:'Onmessage', funx:(message)=>{ if(message.id == requestId ) { // same request send resolve(message) return true } }}) }); } receiveStreamNotifyRoom(funx: Function) { this.ws.registerCallback({ type:'Onmessage', funx:(message)=> { if(message.collection == "stream-notify-room" && message.msg == 'changed') { funx(message) } }}) } getUserStatus(funx:Function){ this.ws.registerCallback({ type:'Onmessage', funx:(message)=>{ if(message.msg == 'changed' && message.collection == "stream-notify-logged") { funx(message) } } }) } loadHistory(roomId, limit: number = 50) { const requestId = uuidv4() const message = { msg: "method", method: "loadHistory", id: requestId, params: [ roomId, null, limit, { "$date": 1480377601 } ] } this.ws.send({message, requestId: 'loadHistory'}) return new Promise((resolve, reject) => { this.ws.registerCallback({type:'Onmessage', funx:(message)=>{ // if(message.id == requestId ) { // same request send resolve(message) return true } }}) }); } setStatus(status: 'online' | 'busy' | 'away' | 'offline') { const requestId = uuidv4() const message = { msg: "method", method: `UserPresence:setDefaultStatus`, id: requestId, params: [ status ] } this.ws.send({message, requestId}) } subscribeNotifyRoom(roomId : string) { const requestId = uuidv4() var message = { "msg": "sub", "id": requestId, "name": "stream-notify-room", "params":[ `${roomId}/event`, false ] } this.ws.send({message, requestId}); return new Promise((resolve, reject) => { this.ws.registerCallback({type:'Onmessage', funx:(message)=>{ if(message.msg == 'ready' || deepFind(message, 'subs.0') == requestId) { // same request send resolve(message) return true } }}) }); } updateRoomEventss(roomId, collection:string, funx: Function, ) { this.ws.registerCallback({ type:'Onmessage', funx:(message)=>{ // if(message.msg =='changed' && message.collection == collection) { if(message.fields.args[0].rid == roomId) { funx(message) } else if(message.fields.eventName ==`${roomId}/deleteMessage`){ funx(message) } } } }) } streamRoomMessages(roomId : string) { const requestId = uuidv4() const message = { "msg": "sub", "id": requestId, "name": "stream-room-messages", "params":[ `${roomId}`, false ] } this.ws.send({message, requestId}); return new Promise((resolve, reject) => { this.ws.registerCallback({type:'Onmessage', funx:(message)=>{ if(message.id == requestId) { // same request send resolve(message) return true } }}) }); } streamNotifyLogged() { const requestId = uuidv4() const message = { "msg": "sub", "id": requestId, "name": "stream-notify-logged", "params":[ "user-status", false ] } this.ws.send({message, requestId}); return new Promise((resolve, reject) => { this.ws.registerCallback({type:'Onmessage', funx:(message)=>{ if(message.id == requestId) { // same request send resolve(message) return true } }}) }); } streamNotifyRoom(roomId : string) { const requestId = uuidv4() let message = { "msg": "method", "method": "stream-notify-room", "id": requestId, "params": [ `null/typing`, "paulo.pinto", true ] }; this.ws.send({message, requestId}) return new Promise((resolve, reject) => { this.ws.registerCallback({type:'Onmessage', funx:(message)=>{ if(message.id == requestId ) { // same request send resolve(message) return true } }}) }); } streamNotifyRoomDeleteMessage(roomId:string) { const requestId = uuidv4() let message = { "msg": "sub", "id": requestId, "name": "stream-notify-room", "params": [ `${roomId}/deleteMessage`, true ] }; this.ws.send({message, requestId}) return new Promise((resolve, reject) => { this.ws.registerCallback({type:'Onmessage', funx:(message)=>{ if(message.id == requestId ) { // same request send resolve(message) return true } }}) }); } registerCallback(data:wsCallbacksParams) { return this.ws.registerCallback(data) } // socket class ================================================================== private socket!: WebSocket; private wsMsgQueue : {[key: string]: msgQueue} = {} private wsCallbacks: {[key: string]: wsCallbacksParams} = {} private wsReconnect = 0 private n = 0 private ws = { connected: false, registerCallback:(params: wsCallbacksParams) => { let id = params.requestId || params.key || uuidv4() // this.wsCallbacks[id] = params this.n++ return id }, connect:()=> { this.socket = new WebSocket(environment.apiWsChatUrl); // bind function this.socket.onopen = this.ws.onopen; this.socket.onmessage = this.ws.onmessage; this.socket.onclose = this.ws.onclose; this.socket.onerror = this.ws.onerror; }, onopen: async ()=> { this.ws.connected = true setTimeout(()=>{ this.ws.wsMsgQueue() }, 10) if(this.wsReconnect >= 1) { for (const [key, value] of Object.entries(this.wsCallbacks)) { if(value.type== 'reConnect') { const dontRepeat = await value.funx() if(dontRepeat) { this.n-- delete this.wsCallbacks[key] } } } } this.wsReconnect++; }, wsMsgQueue:()=> { for (const [key, item] of Object.entries(this.wsMsgQueue)) { if(item.loginRequired == true && this.isLogin == true) { // this.ws.send(item); delete this.wsMsgQueue[key] } else if(item.loginRequired == false) { // this.ws.send(item); delete this.wsMsgQueue[key] } } }, send: ({message, requestId = uuidv4(), loginRequired = true}:send) => { if (this.ws.connected == false || loginRequired == true && this.isLogin == false) { // save data to send when back online // // this.wsMsgQueue[requestId] = {message, requestId, loginRequired} } else { let messageStr = JSON.stringify(message) // this.socket.send(messageStr) } return requestId }, onmessage: async (event: any)=> { const data = JSON.parse(event.data) // // console.log(data) for (const [key, value] of Object.entries(this.wsCallbacks)) { if(value.type== 'Onmessage') { const dontRepeat = await value.funx(data) if(dontRepeat) { delete this.wsCallbacks[key] this.n-- } } } }, onclose:(event: any)=> { this.ws.connected = false this.isLogin = false this.connect() this.login() }, onerror: (event: any) => { }, disconnect:() => { if(this.socket) { this.socket.onopen = (event: any) => {} this.socket.onmessage = (event: any) => {} this.socket.onclose = (event: any) => {} this.socket.onerror = (event: any) => {} this.socket.close() } } }}