@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
JavaScript
"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