@bitmartexchange/bitmart-node-sdk-api
Version:
BitMart Exchange official Nodejs client for the BitMart Cloud API.
174 lines (147 loc) • 4.62 kB
JavaScript
'use strict'
const zlib = require('zlib')
const crypto = require('crypto')
const WebSocketClient = require('ws')
const {execSync} = require('child_process');
const {isEmptyValue, createDefaultLogger} = require("./utils");
class CloudWebsocketClient {
constructor (wsURL, isSpot, options = {}) {
this.isSpot = isSpot
this.wsURL = wsURL
this.apiKey = options.apiKey
this.apiSecret = options.apiSecret
this.apiMemo = options.apiMemo
this.callbacks = options.callbacks || {}
this.reconnectDelay = options.reconnectDelay || 5000
this.logger = options.logger || createDefaultLogger(false)
this.wsConnection = {}
}
isConnected () {
if (!this.wsConnection.ws || this.wsConnection.ws.readyState !== WebSocketClient.OPEN) return false
return true
}
initConnect () {
const ws = new WebSocketClient(this.wsURL)
this.logger.debug(`Sending Websocket connection to: ${this.wsURL}`)
this.wsConnection.ws = ws
this.wsConnection.closeInitiated = false
ws.on('open', () => {
this.logger.debug(`Connected to the Websocket Server: ${this.wsURL}`)
this.callbacks.open && this.callbacks.open(this)
setInterval(() => {
this.keepAlive()
}, 5000)
})
ws.on('message', (data, isBinary) => {
if (isBinary) {
const decompressedData = zlib.inflateRawSync(data);
this.callbacks.message && this.callbacks.message(decompressedData.toString())
} else {
if (data.toString() === 'pong') {
this.callbacks.pong && this.callbacks.pong()
return
}
this.callbacks.message && this.callbacks.message(data.toString())
}
})
ws.on('ping', () => {
// this.logger.info('Received PING from server')
this.callbacks.ping && this.callbacks.ping()
})
ws.on('pong', () => {
// this.logger.info('Received PONG from server')
this.callbacks.pong && this.callbacks.pong()
})
ws.on('error', err => {
this.logger.error('Received error from server')
this.callbacks.error && this.callbacks.error()
this.logger.error(err)
})
ws.on('close', (closeEventCode, reason) => {
if (!this.wsConnection.closeInitiated) {
this.callbacks.close && this.callbacks.close()
this.logger.warn(`Connection close due to ${closeEventCode}: ${reason}.`)
setTimeout(() => {
this.logger.debug('Reconnect to the server.')
this.initConnect()
}, this.reconnectDelay)
} else {
this.wsConnection.closeInitiated = false
}
})
}
/**
* Unsubscribe the stream <br>
*
* @param {WebSocketClient} wsConnection - websocket client instance created by ws package
*/
disconnect () {
if (!this.isConnected()) this.logger.warn('No connection to close.')
else {
this.wsConnection.closeInitiated = true
this.wsConnection.ws.close()
this.logger.debug('Disconnected with BitMart Websocket Server')
}
}
keepAlive() {
if (!this.isConnected()) {
this.logger.warn('Send ping can be sent only when connection is ready.')
}
else {
if (this.isSpot) {
this.wsConnection.ws.send('ping')
} else {
this.wsConnection.ws.send('{"action":"ping"}')
}
}
}
/**
* Send message
* @param {JSON} message
*/
send (message) {
if (!this.isConnected()) this.logger.warn('Send only can be sent when connection is ready.')
else {
this.wsConnection.ws.send(message)
}
}
login() {
if (!this.isConnected()) {
this.logger.error('Not connected')
return
}
if(isEmptyValue(this.apiKey)) {
this.logger.error('api key miss')
return
}
if(isEmptyValue(this.apiSecret)) {
this.logger.error('api secret miss')
return
}
if(isEmptyValue(this.apiMemo)) {
this.logger.error('api memo miss')
return
}
const timestamp = Date.now()
const sign = crypto
.createHmac('sha256', this.apiSecret)
.update(timestamp + "#" + this.apiMemo + "#" + "bitmart.WebSocket")
.digest('hex')
let loginMessage = {}
if(this.isSpot) {
loginMessage = {
op: 'login',
args: [this.apiKey, timestamp, sign],
}
} else {
loginMessage = {
action: 'access',
args: [this.apiKey, timestamp + "", sign, 'web'],
}
}
this.send(JSON.stringify(loginMessage))
this.logger.debug('logging in....')
execSync('sleep 2')
}
}
module.exports = CloudWebsocketClient