UNPKG

ivcbox-adapter

Version:
779 lines 29 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var SockJS = require("sockjs-client"); var stomp = require("stompjs"); var log_1 = require("./services/log"); var common_1 = require("./services/common"); var _GLOBAL = {}; var Base = /** @class */ (function () { function Base() { this.CONNECTION_STATUSES = { notConnected: 'not_connected', connecting: 'connecting', connected: 'connected' }; /** * Stores the Signaling user credentials from the API response required for connecting to the Signaling server. */ this.user = { refreshToken: null, sessionKey: null }; /** * Stores the stom object */ this.stompClient = null; /** * Stores connection status */ this.socketConnectionStatus = this.CONNECTION_STATUSES.notConnected; /** * Stores the debugging Signaling server. */ this.socketServer = 'https://signal2.ivcbox.com/vcws/ws/'; /** * Stores the current socket connection information. */ this.socketSession = null; /** * Stores the Signaling socket connection object. */ this.socket = null; /** * Stores the App Key configured in <code>init()</code>. */ this.appKey = null; /** * Stores the list of <code>on()</code> event handlers. */ this.EVENTS = {}; /** * Stores the list of <code>once()</code> event handlers. * These events are only triggered once. */ this.onceEvents = {}; /** * Stores the timestamps data used for throttling. */ this.timestamp = {}; this.heartbeatOutTimer = null; /** * Stores amount of tick without heartbeat */ this.heartbeatInCounter = 0; /** * Interval for increasing _heartbeatInCounter */ this.heartbeatInTimer = null; /** * Counter for reconnecting */ this.reconnectCounter = 0; /** * Connection type for init connection */ this.connectionType = 0; } Base.prototype.setAppKey = function (appKey) { var self = this; if (typeof appKey === 'string') { self.appKey = appKey; } else { self.appKey = null; } }; Base.prototype.disconnect = function (callback, resetConnectStatus) { if (callback === void 0) { callback = null; } if (resetConnectStatus === void 0) { resetConnectStatus = true; } var self = this; self.off(); self.clearIntervals(); if (self.stompClient) { self.stompClient.disconnect(function () { if (resetConnectStatus) { self.socketConnectionStatus = self.CONNECTION_STATUSES.notConnected; } self.socketSession = null; self.stompClient = null; if (callback && typeof callback === 'function') { callback(); } }); } }; /** * Инициализация подключения к серверу текущего пользователя */ Base.prototype.initConnection = function (payload) { var self = this; if (payload.user) { self.user = payload.user; } if (!self.stompClient) { log_1.default.error('You are not connected'); return; } if (!self.user.sessionKey) { log_1.default.error('User session key is required'); return; } self.stompClient.send('/app/ws/initConnection', {}, JSON.stringify({ sessionKey: self.user.sessionKey, connectionType: typeof payload.connectionType === 'number' ? payload.connectionType : 0, connectionInfo: payload.connectionInfo || {} })); }; Base.prototype.createUser = function (payload) { if (payload === void 0) { payload = {}; } var self = this; if (!self.stompClient) { log_1.default.error('You are not connected'); return; } self.stompClient.send('/app/ws/user/create', {}, JSON.stringify(payload)); }; Base.prototype.setUserInfo = function (payload) { var self = this; self.stompClient.send('/app/ws/user/setInfo', {}, JSON.stringify(payload)); }; Base.prototype.subscribeToUser = function (userId) { var self = this; self.stompClient.send('/app/ws/user/subscribe', {}, JSON.stringify({ userId: userId })); }; Base.prototype.unsubscribeFromUser = function (userId) { var self = this; self.stompClient.send('/app/ws/user/unsubscribe', {}, JSON.stringify({ userId: userId })); }; Base.prototype.sendMessageToUser = function (userId, payload) { var self = this; if (typeof userId !== 'number') { log_1.default.error('wrong userId'); return; } if (typeof payload !== 'object' || typeof payload.eventName !== 'string') { log_1.default.error('eventName must be a string'); return; } var requestObj = { toUser: userId, payload: payload }; self.stompClient.send('/app/ws/user/message', {}, JSON.stringify(requestObj)); }; Base.prototype.sendMessageToBot = function (botType, payload) { var self = this; if (typeof botType !== 'number' || botType < 1) { log_1.default.error('wrong bot type'); return; } if (typeof payload !== 'object' || typeof payload.eventName !== 'string') { log_1.default.error('eventName must be a string'); return; } var requestObj = { type: botType, payload: payload }; self.stompClient.send('/app/ws/signalToBot', {}, JSON.stringify(requestObj)); }; Base.prototype.createRoom = function (payload) { var self = this; if (typeof payload.topic !== 'number') { log_1.default.error('topic must be a number'); return; } if (self.appKey === null) { log_1.default.error('no appKey'); return; } var requestObj = { appKey: self.appKey, roomTopicId: payload.topic, roomName: payload.roomName }; self.stompClient.send('/app/ws/room/create', {}, JSON.stringify(requestObj)); }; Base.prototype.setRoomName = function (payload) { var self = this; if (typeof payload.roomHash !== 'string') { log_1.default.error('roomHash must be a string'); return; } if (typeof payload.name !== 'string') { log_1.default.error('room name must be a string'); return; } var requestObj = { roomHash: payload.roomHash, name: payload.name }; self.stompClient.send('/app/ws/room/setName', {}, JSON.stringify(requestObj)); }; Base.prototype.joinRoom = function (payload) { var self = this; if (typeof payload.roomHash !== 'string') { log_1.default.error('roomHash must be a string'); return; } var requestObj = { roomHash: payload.roomHash }; self.stompClient.send('/app/ws/room/join', {}, JSON.stringify(requestObj)); }; Base.prototype.leaveRoom = function (payload) { var self = this; if (typeof payload.roomHash !== 'string') { log_1.default.error('roomHash must be a string'); return; } var requestObj = { roomHash: payload.roomHash }; self.stompClient.send('/app/ws/room/leave', {}, JSON.stringify(requestObj)); }; Base.prototype.closeRoom = function (payload) { var self = this; if (typeof payload.roomHash !== 'string') { log_1.default.error('roomHash must be a string'); return; } var requestObj = { roomHash: payload.roomHash }; self.stompClient.send('/app/ws/room/close', {}, JSON.stringify(requestObj)); }; Base.prototype.openRoom = function (payload) { var self = this; if (typeof payload.roomHash !== 'string') { log_1.default.error('roomHash must be a string'); return; } var requestObj = { roomHash: payload.roomHash }; self.stompClient.send('/app/ws/room/open', {}, JSON.stringify(requestObj)); }; Base.prototype.switchRoomTopic = function (payload) { var self = this; if (typeof payload.roomHash !== 'string') { log_1.default.error('roomHash must be a string'); return; } if (typeof payload.topicId !== 'number') { log_1.default.error('topic must be a number'); return; } var refreshStatus = payload.refreshStatus !== 1 ? 0 : 1; var requestObj = { roomHash: payload.roomHash, topicId: payload.topicId, refreshStatus: refreshStatus }; self.stompClient.send('/app/ws/room/switchTopic', {}, JSON.stringify(requestObj)); }; Base.prototype.sendMessage = function (payload) { var self = this; if (typeof payload.roomHash !== 'string') { log_1.default.error('roomHash must be a string'); return; } if (typeof payload.message !== 'object') { log_1.default.error('message must be an object'); return; } var requestObj = { roomHash: payload.roomHash, message: payload.message }; self.stompClient.send('/app/ws/room/sendMessage', {}, JSON.stringify(requestObj)); }; Base.prototype.sendMessageTyping = function (payload) { var self = this; if (typeof payload.roomHash !== 'string') { log_1.default.error('roomHash must be a string'); return; } var requestObj = { roomHash: payload.roomHash }; self.stompClient.send('/app/ws/room/messageTyping', {}, JSON.stringify(requestObj)); }; Base.prototype.sendLastSeenMessage = function (payload) { var self = this; if (typeof payload.roomHash !== 'string') { log_1.default.error('roomHash must be a string'); return; } if (typeof payload.messageId !== 'number') { log_1.default.error('messageId must be a number'); return; } var requestObj = { roomHash: payload.roomHash, messageId: payload.messageId }; self.stompClient.send('/app/ws/room/lastSeenMessage', {}, JSON.stringify(requestObj)); }; Base.prototype.sendRoomBroadcast = function (roomHash, payload) { var self = this; if (typeof roomHash !== 'string') { log_1.default.error('roomHash must be a string'); return; } if (typeof payload !== 'object' || typeof payload.eventName !== 'string') { log_1.default.error('message must be an object'); return; } var requestObj = { roomHash: roomHash, payload: payload }; self.stompClient.send('/app/ws/room/broadcast', {}, JSON.stringify(requestObj)); }; Base.prototype.checkTopicOnline = function (payload) { var self = this; if (typeof payload.topic !== 'number') { log_1.default.error('topic must be a number'); return; } var requestObj = { topicId: payload.topic }; self.stompClient.send('/app/ws/checkTopicOnline', {}, JSON.stringify(requestObj)); }; /** * Function that subscribes a listener to an event once. */ Base.prototype.once = function (eventName, callback, condition) { var conditionFunc = (typeof condition !== 'function') ? function () { return true; } : condition; if (typeof callback === 'function') { this.onceEvents[eventName] = this.onceEvents[eventName] || []; this.onceEvents[eventName].push([callback, conditionFunc]); log_1.default.console(['Event', eventName, 'Event is subscribed on condition']); } }; /** * Function that subscribes a listener to an event. */ Base.prototype.on = function (eventName, callback) { if (typeof callback === 'function') { this.EVENTS[eventName] = this.EVENTS[eventName] || []; this.EVENTS[eventName].push(callback); log_1.default.console(['Event', eventName, 'Event is subscribed']); } }; /** * Function that unsubscribes listeners from an event. */ Base.prototype.off = function (eventName) { if (!(eventName && typeof eventName === 'string')) { this.EVENTS = {}; this.onceEvents = {}; } else { this.EVENTS[eventName] = []; this.onceEvents[eventName] = []; } }; /** * Function that triggers an event. * The rest of the parameters after the <c>eventName</c> parameter is considered as the event parameter payloads. */ Base.prototype.trigger = function (eventName) { var rest = []; for (var _i = 1; _i < arguments.length; _i++) { rest[_i - 1] = arguments[_i]; } // convert the arguments into an array var args = Array.prototype.slice.call(arguments); args.shift(); // Omit the first argument since it's the event name var arr = this.EVENTS[eventName]; var once = this.onceEvents[eventName] || null; if (arr) { // for events subscribed forever for (var _a = 0, arr_1 = arr; _a < arr_1.length; _a++) { var event_1 = arr_1[_a]; try { if (event_1.apply(this, args) === false) { break; } } catch (error) { log_1.default.error(['Event', eventName, 'Exception occurred in event:', error]); } } } if (once) { // for events subscribed on once for (var j = 0; j < once.length; j++) { if (once[j][1].apply(this, args) === true) { log_1.default.console(['Event', eventName, 'Condition is met. Firing event']); if (once[j][0].apply(this, args) === false) { break; } if (once[j] && !once[j][2]) { log_1.default.console(['Event', eventName, 'Removing event after firing once']); once.splice(j, 1); // After removing current element, the next element should be element of the same index j--; } } else { log_1.default.console(['Event', eventName, 'Condition is still not met. ' + 'Holding event from being fired']); } } } }; /** * Function that starts an interval check to wait for a condition to be resolved. */ Base.prototype.wait = function (callback, condition, intervalTime) { if (typeof callback === 'function' && typeof condition === 'function') { if (condition()) { log_1.default.console(['Event', null, 'Condition is met. Firing callback']); callback(); return; } log_1.default.console(['Event', null, 'Condition is not met. Doing a check.']); var intervalTimeR = (typeof intervalTime === 'number') ? intervalTime : 50; var doWait_1 = setInterval(function () { if (condition()) { log_1.default.console(['Event', null, 'Condition is met after waiting. Firing callback']); clearInterval(doWait_1); callback(); } }, intervalTimeR); } }; /** * Function that throttles a method function to prevent multiple invokes over a specified amount of time. * Returns a function to be invoked <code>.throttle(fn, 1000)()</code> to make throttling functionality work. */ Base.prototype.throttle = function (func, prop, wait) { var self = this; var now = (new Date()).getTime(); if (!(self.timestamp[prop] && ((now - self.timestamp[prop]) < wait))) { func(true); self.timestamp[prop] = now; } else { func(false); } }; /** * Base initialization */ Base.prototype.initialization = function (options, callback) { if (callback === void 0) { callback = null; } var self = this; if (self.socketConnectionStatus === self.CONNECTION_STATUSES.connecting) { return; } var prevSocketConnectionStatus = self.socketConnectionStatus; self.socketConnectionStatus = self.CONNECTION_STATUSES.connecting; if (typeof options.socketServer === 'string') { self.socketServer = options.socketServer; } if (typeof options.appKey === 'string') { self.appKey = options.appKey; } var resultFunc = function () { if (typeof callback === 'function') { var readyStateChangeFn = function () { callback(); }; self.once('connectionSucceeded', readyStateChangeFn); } self.trigger('readyStateChange'); self.initSocketConnection(); }; if (self.stompClient && prevSocketConnectionStatus === self.CONNECTION_STATUSES.connected) { self.disconnect(resultFunc, false); } else { resultFunc(); } }; /** * Starts initialising for Room credentials for room name provided in <code>joinRoom()</code> method. */ Base.prototype.initSocketConnection = function () { var self = this; if (self.socketConnectionStatus === self.CONNECTION_STATUSES.connected) { return; } var sockJs = SockJS; if (!common_1.isConstructor(SockJS)) { sockJs = SockJS.default; } var socket = new sockJs(self.socketServer, null, { sessionId: function () { self.socketSession = Math.round((new Date()).getTime() + Math.random() * 1000000); return self.socketSession; } }); self.socket = socket; self.stompClient = stomp.over(socket); self.stompClient.heartbeat.outgoing = 10000; self.stompClient.heartbeat.incoming = 10000; self.stompClient.reconnect_delay = 0; // @ts-ignore if (process && process.env && process.env.NODE_ENV === 'production') { self.stompClient.debug = function () { // nothing }; } self.stompClient.connect({}, function () { self.socketConnectionStatus = self.CONNECTION_STATUSES.connected; self.reconnectCounter = 0; self.stompClient.subscribe('/user/queue/actions', function (messageOutput) { self.processAction(messageOutput.headers, JSON.parse(messageOutput.body)); }); // heartbeat self.stompClient.subscribe('/topic/heartbeat', function () { self.heartbeatInCounter = 0; }); self.stompClient.subscribe('/topic/systemMessage', function (messageOutput) { self.processSystemMessage(JSON.parse(messageOutput.body)); }); if (self.heartbeatOutTimer === null) { _GLOBAL[self.heartbeatOutTimer] = setInterval(function () { if (self.socket.readyState === 1) { self.stompClient.send('/app/ws/heartbeat', {}, null); } }, 15000); } if (self.heartbeatOutTimer === null) { _GLOBAL[self.heartbeatInTimer] = setInterval(function () { self.heartbeatInCounter++; if (self.socketConnectionStatus === self.CONNECTION_STATUSES.connected) { if (self.heartbeatInCounter > 2 && self.heartbeatInCounter < 5) { self.autoReconnect(); } else if (self.heartbeatInCounter === 5) { self.trigger('noHeartbeat'); self.heartbeatInCounter = 0; self.disconnect(); } } }, 10000); } self.trigger('connectionSucceeded', self.stompClient); }, function () { if (self.socketConnectionStatus !== self.CONNECTION_STATUSES.notConnected) { self.socketConnectionStatus = self.CONNECTION_STATUSES.notConnected; setTimeout(function () { if (self.reconnectCounter < 2) { self.reconnectCounter++; self.autoReconnect(); } else { self.reconnectCounter = 0; self.trigger('connectionFailed'); self.disconnect(); } }, 1000); } }); }; Base.prototype.processAction = function (messageHeaders, messageBody) { var self = this; var headers = messageHeaders; var data = messageBody.data; switch (messageBody.action) { // common case 'initConnectionResponse': self.processInitConnection(data); break; case 'exception': self.processException(headers.sourceMessage, data); break; case 'invalidateSession': self.processInvalidateSession(); break; // user case 'user': self.processUser(data); break; case 'createUserResponse': self.processCreateUser(data); break; case 'messageFromUser': self.processMessageFromUser(data); break; // room case 'room': self.processRoom(data); break; case 'roomMessage': self.processMessage(data); break; case 'roomBroadcast': self.processRoomBroadcast(data); break; case 'roomMessageTyping': self.processMessageTyping(data); break; // other case 'checkTopicOnlineResponse': self.processCheckTopicOnline(data); break; case 'signalFromBot': self.processMessageFromBot(data); break; default: self.trigger(messageBody.action + 'Signal', data); break; } }; Base.prototype.autoReconnect = function () { var self = this; self.trigger('autoReconnect'); }; Base.prototype.clearIntervals = function () { var self = this; if (self.heartbeatInTimer) { clearInterval(self.heartbeatInTimer); } }; Base.prototype.processInitConnection = function (payload) { var self = this; if (payload.sessionKey) { self.user.sessionKey = payload.sessionKey; } if (payload.refreshToken) { self.user.refreshToken = payload.refreshToken; } self.trigger('initConnectionResponse', payload); }; Base.prototype.reinitConnection = function (payload) { var self = this; if (!self.stompClient) { log_1.default.error('You are not connected'); return; } if (!payload.refreshToken) { log_1.default.error('No refresh token'); return; } var requestObj = { refreshToken: payload.refreshToken, connectionType: self.connectionType }; self.stompClient.send('/app/ws/reinitConnection', {}, JSON.stringify(requestObj)); }; Base.prototype.processCreateUser = function (payload) { var self = this; self.user = { sessionKey: payload.sessionKey, refreshToken: payload.refreshToken }; self.trigger('createUserResponse', payload); }; Base.prototype.processException = function (sourceMessage, payload) { var self = this; switch (sourceMessage) { case '/app/ws/initConnection': self.user.sessionKey = null; switch (payload.message) { case 'WRONG_SESSION_KEY': self.trigger('wrongSessionKey'); break; case 'OUTDATED_SESSION_KEY': self.trigger('outdatedSessionKey'); if (typeof self.user.refreshToken === 'string') { self.reinitConnection({ refreshToken: self.user.refreshToken }); } break; case 'USER_NOT_FOUND': self.trigger('wrongUser'); break; default: console.error(sourceMessage, payload.message); break; } break; case '/app/ws/reinitConnection': self.user.refreshToken = null; switch (payload.message) { case 'WRONG_REFRESH_TOKEN': self.trigger('wrongRefreshToken'); break; case 'OUTDATED_REFRESH_TOKEN': self.trigger('outdatedRefreshToken'); break; default: console.error(sourceMessage, payload.message); break; } break; case '/app/ws/user/create': switch (payload.message) { case 'INVALID_DASHBOARDUSERTOKEN': self.trigger('invalidDashboardUserToken'); break; default: console.error(sourceMessage, payload.message); break; } break; default: console.error(sourceMessage, payload.message); break; } }; Base.prototype.processInvalidateSession = function () { var self = this; self.trigger('invalidateSession'); self.disconnect(); }; Base.prototype.processUser = function (payload) { var self = this; self.trigger('user', payload); }; Base.prototype.processMessageFromUser = function (payload) { var self = this; self.trigger('userMessage', payload); }; Base.prototype.processRoom = function (payload) { var self = this; self.trigger('room', payload); }; Base.prototype.processMessage = function (payload) { var self = this; self.trigger('roomMessage', payload); }; Base.prototype.processRoomBroadcast = function (payload) { var self = this; self.trigger('roomBroadcast', payload); }; Base.prototype.processMessageTyping = function (payload) { var self = this; self.trigger('roomMessageTyping', payload); }; Base.prototype.processSystemMessage = function (payload) { var self = this; self.trigger('systemMessage', payload); }; Base.prototype.processCheckTopicOnline = function (payload) { var self = this; self.trigger('checkTopicOnline', payload); }; Base.prototype.processMessageFromBot = function (payload) { var self = this; self.trigger('botMessage', payload); }; return Base; }()); exports.default = Base; //# sourceMappingURL=Base.js.map