UNPKG

@fraserdarwent/xapi-node

Version:

This project is made possible to get data from Forex market, execute market or limit order with NodeJS/JS through WebSocket connection

200 lines 8.94 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SocketConnection = void 0; const __1 = require("../.."); const WebSocketWrapper_1 = require("../../modules/WebSocketWrapper"); const Log_1 = require("../../utils/Log"); const Enum_1 = require("../../enum/Enum"); const Queue_1 = require("../Queue"); class SocketConnection extends Queue_1.Queue { constructor(XAPI, password, url) { super(XAPI.rateLimit, Enum_1.TransactionType.SOCKET); this.loginTimeout = new __1.Timer(); this.pingTimeout = new __1.Timer(); this.XAPI = XAPI; this.WebSocket = new WebSocketWrapper_1.WebSocketWrapper(url); this._password = password; this.WebSocket.onOpen(() => this.setConnectionStatus(Enum_1.ConnectionStatus.CONNECTING)); this.WebSocket.onClose(() => this.setConnectionStatus(Enum_1.ConnectionStatus.DISCONNECTED)); this.WebSocket.onMessage((json) => { this.lastReceivedMessage = new __1.Time(); try { const message = JSON.parse(json.toString().trim()); try { this.handleSocketMessage(message, new __1.Time()); } catch (e) { const { name, message, stack } = new Error(e); Log_1.Log.error('Socket WebSocket Handle Message ERROR'); Log_1.Log.hidden(name + '\n' + message + (stack ? '\n' + stack : ''), 'ERROR'); } } catch (e) { const { name, message, stack } = new Error(e); Log_1.Log.error('Socket WebSocket JSON parse ERROR'); Log_1.Log.hidden(name + '\n' + message + (stack ? '\n' + stack : '') + '\n\n' + json, 'ERROR'); } }); this.WebSocket.onError((error) => { const { name, message, stack } = new Error(error); Log_1.Log.error('Socket WebSocket ERROR'); Log_1.Log.hidden(name + '\n' + message + (stack ? '\n' + stack : ''), 'ERROR'); }); } connect() { this.WebSocket.connect(); } onConnectionChange(callBack, key = null) { this.addListener(Enum_1.Listeners.xapi_onConnectionChange, callBack, key); } setConnectionStatus(status) { this.resetMessageTube(); this.openTimeout.clear(); this.pingTimeout.clear(); this.loginTimeout.clear(); this.status = status; if (status === Enum_1.ConnectionStatus.CONNECTING) { this.pingTimeout.setTimeout(() => { this.ping().catch(e => { Log_1.Log.error('Socket: ping request failed (SocketConnection.ts:67)'); }); }, 100); this.openTimeout.setTimeout(() => { this.status = Enum_1.ConnectionStatus.CONNECTED; this.tryLogin(2); }, 1000); } else { for (const transactionId in this.transactions) { const isInterrupted = (this.transactions[transactionId].status === Enum_1.TransactionStatus.sent); if (this.transactions[transactionId].status === Enum_1.TransactionStatus.waiting || isInterrupted) { this.rejectTransaction({ code: Enum_1.errorCode.XAPINODE_1, explain: 'Socket closed' }, this.transactions[transactionId], isInterrupted); } } } } tryLogin(retries = 2) { this.login().catch(e => { Log_1.Log.error('Login is rejected (userId = ' + this.XAPI.accountId + ', accountType = ' + this.XAPI.accountType + ')\nReason:\n' + JSON.stringify(e, null, '\t'), 'ERROR'); if (retries > 0 && e.reason.code !== Enum_1.errorCode.XAPINODE_1 && e.reason.code !== Enum_1.errorCode.BE005) { this.loginTimeout.setTimeout(() => { Log_1.Log.hidden('Try to login (retries = ' + retries + ')', 'INFO'); this.tryLogin(retries - 1); }, 500); } else if (e.reason.code === Enum_1.errorCode.BE005) { Log_1.Log.error('Disconnect from stream and socket (reason = \'login error code is ' + e.reason.code + '\')'); this.XAPI.disconnect(); } this.XAPI.callListener(Enum_1.Listeners.xapi_onReject, [e]); }); } handleError(code, explain, customTag, received) { const { transactionId } = __1.Utils.parseCustomTag(customTag); if (transactionId !== null && this.transactions[transactionId] !== undefined) { this.rejectTransaction({ code, explain }, this.transactions[transactionId], false, received); } else { Log_1.Log.hidden('Socket error message:\n' + JSON.stringify({ code, explain, customTag }, null, '\t'), 'ERROR'); } } handleSocketMessage(message, time) { if (message.status) { const returnData = message.streamSessionId === undefined ? message.returnData : { streamSessionId: message.streamSessionId }; const customTag = typeof (message.customTag) === 'string' ? message.customTag : null; const { transactionId, command } = __1.Utils.parseCustomTag(customTag); if (transactionId !== null && command !== null && this.transactions[transactionId] !== undefined) { this.resolveTransaction(returnData, time, this.transactions[transactionId]); this.callListener('command_' + command, [returnData, time, this.transactions[transactionId]]); } else { Log_1.Log.error('Received a message without vaild customTag (customTag = ' + customTag + ')\n' + JSON.stringify(message, null, '\t')); } } else if (message.status !== undefined && message.errorCode !== undefined) { const { errorCode } = message; const customTag = message.customTag === undefined ? null : message.customTag; const errorDescr = message.errorDescr === undefined ? null : message.errorDescr; this.handleError(errorCode, errorDescr, customTag, time); } } sendCommand(command, args = {}, transactionId = null, urgent = false) { return new Promise((resolve, reject) => { if (transactionId === null) { transactionId = this.createTransactionId(); } const transaction = this.addTransaction({ command, json: JSON.stringify({ command, arguments: (Object.keys(args).length === 0) ? undefined : args, customTag: command + '_' + transactionId }), args, transactionId, urgent, resolve, reject }); if (transaction.request.json.length > 1000) { this.rejectTransaction({ code: Enum_1.errorCode.XAPINODE_0, explain: 'Each command invocation should not contain more than 1kB of data.' }, transaction); } else if (this.status === Enum_1.ConnectionStatus.DISCONNECTED) { this.rejectTransaction({ code: Enum_1.errorCode.XAPINODE_1, explain: 'Socket closed' }, transaction); } else if (this.XAPI.Stream.session.length === 0 && 'login' !== command && 'ping' !== command && 'logout' !== command) { this.rejectTransaction({ code: Enum_1.errorCode.XAPINODE_BE103, explain: 'User is not logged' }, transaction); } else if (this.XAPI.isTradingDisabled && command === 'tradeTransaction') { this.rejectTransaction({ code: Enum_1.errorCode.XAPINODE_4, explain: 'Trading disabled in login config (safe = true)' }, transaction); } else { this.sendMessage(transaction, true); } }); } closeConnection() { this.WebSocket.close(); } ping() { return this.sendCommand('ping', {}, null, true); } logout() { return this.sendCommand('logout', {}, null, true); } login() { return this.sendCommand('login', { 'userId': this.XAPI.accountId, 'password': this._password, 'appName': this.XAPI.appName }, null, true); } } exports.SocketConnection = SocketConnection; //# sourceMappingURL=SocketConnection.js.map