@sharplygroup/xtb-api-js
Version:
A module for interacting with the XTB API
177 lines • 6.26 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebSocketManager = void 0;
const ws_1 = __importDefault(require("ws"));
const XtbLoginError_1 = require("../errors/XtbLoginError");
const XtbConnectionError_1 = require("../errors/XtbConnectionError");
class WebSocketManager {
mainSocket = null;
streamSocket = null;
pingInterval = null;
streamSessionId = null;
getStreamSessionId() {
return this.streamSessionId;
}
credentials;
isDemo;
constructor(credentials) {
this.credentials = credentials;
this.isDemo = credentials.demo;
}
getMainSocketUrl() {
const host = "wss://ws.xapi.pro";
return this.isDemo ? `${host}/demo` : `${host}/real`;
}
getStreamSocketUrl() {
const host = "wss://ws.xapi.pro";
return this.isDemo ? `${host}/demoStream` : `${host}/realStream`;
}
async connect() {
try {
// Connect main socket
const mainSocket = new ws_1.default(this.getMainSocketUrl());
this.mainSocket = mainSocket;
await this.waitForConnection(mainSocket);
// Login
const loginResponse = await this.login();
if (!loginResponse.status) {
throw new XtbLoginError_1.XtbLoginError(loginResponse.errorDescr || "Login failed", loginResponse.errorCode);
}
this.streamSessionId = loginResponse.streamSessionId || null;
// Connect stream socket if we have streamSessionId
if (this.streamSessionId) {
const streamSocket = new ws_1.default(this.getStreamSocketUrl());
this.streamSocket = streamSocket;
await this.waitForConnection(streamSocket);
}
// Start ping interval
this.startPingInterval();
}
catch (error) {
await this.disconnect();
throw error;
}
}
waitForConnection(socket) {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new XtbConnectionError_1.XtbConnectionError("Connection timeout"));
}, 30000);
socket.once("open", () => {
clearTimeout(timeout);
resolve();
});
socket.once("error", (error) => {
clearTimeout(timeout);
reject(new XtbConnectionError_1.XtbConnectionError(error.message));
});
});
}
async login() {
const command = {
command: "login",
arguments: {
userId: this.credentials.userId,
password: this.credentials.password,
},
};
return this.sendCommand(command);
}
startPingInterval() {
// Send ping every 10 minutes to keep connection alive
this.pingInterval = setInterval(async () => {
try {
await this.sendCommand({ command: "ping", arguments: {} });
}
catch (error) {
// Handle ping error - maybe reconnect
console.error("Ping failed:", error);
}
}, 10 * 60 * 1000); // 10 minutes
}
async sendCommand(command) {
if (!this.mainSocket || this.mainSocket.readyState !== 1) {
// 1 = OPEN
throw new Error("Main socket not connected");
}
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error("Command timeout"));
}, 30000);
const messageHandler = (data) => {
try {
const response = JSON.parse(data.toString());
this.mainSocket?.removeListener("message", messageHandler);
clearTimeout(timeout);
resolve(response);
}
catch (error) {
this.mainSocket?.removeListener("message", messageHandler);
clearTimeout(timeout);
reject(error);
}
};
if (this.mainSocket) {
this.mainSocket.on("message", messageHandler);
this.mainSocket.send(JSON.stringify(command));
}
else {
reject(new Error("Socket is not initialized"));
}
});
}
async sendStreamCommand(command) {
if (!this.streamSocket || this.streamSocket.readyState !== 1) {
// 1 = OPEN
throw new Error("Stream socket not connected");
}
if (!this.streamSessionId) {
throw new Error("No stream session ID available");
}
const streamCommand = {
...command,
streamSessionId: this.streamSessionId,
};
this.streamSocket.send(JSON.stringify(streamCommand));
}
onStreamMessage(callback) {
if (!this.streamSocket) {
throw new Error("Stream socket not connected");
}
this.streamSocket.on("message", (data) => {
try {
const message = JSON.parse(data.toString());
callback(message);
}
catch (error) {
console.error("Error parsing stream message:", error);
}
});
}
async disconnect() {
if (this.pingInterval) {
clearInterval(this.pingInterval);
this.pingInterval = null;
}
if (this.mainSocket) {
try {
await this.sendCommand({ command: "logout", arguments: {} });
}
catch {
// Ignore logout errors
}
this.mainSocket.close();
this.mainSocket = null;
}
if (this.streamSocket) {
this.streamSocket.close();
this.streamSocket = null;
}
this.streamSessionId = null;
}
}
exports.WebSocketManager = WebSocketManager;
//# sourceMappingURL=WebSocketManager.js.map