ivcbox-adapter
Version:
779 lines • 29 kB
JavaScript
"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