def __init__(self, subscriptions: List[Subscription], api_key: str = None, sec_key: str = None, ssl_context=None) -> None: super().__init__(websocket_uri=self.WEBSOCKET_URI, subscriptions=subscriptions, builtin_ping_interval=None, periodic_timeout_sec=10, ssl_context=ssl_context) self.api_key = api_key self.sec_key = sec_key self.ping_checker = PeriodicChecker(period_ms=60 * 1000)
class BitforexWebsocket(WebsocketMgr): WEBSOCKET_URI = "wss://www.bitforex.com/mkapi/coinGroup1/ws" PING_MSG = 'ping_p' PONG_MSG = 'pong_p' def __init__(self, subscriptions: List[Subscription], ssl_context=None) -> None: super().__init__(websocket_uri=BitforexWebsocket.WEBSOCKET_URI, subscriptions=subscriptions, builtin_ping_interval=None, periodic_timeout_sec=10, ssl_context=ssl_context) self.ping_checker = PeriodicChecker(period_ms=30 * 1000) async def _process_periodic(self, websocket: Websocket) -> None: if self.ping_checker.check(): LOG.debug(f"> {BitforexWebsocket.PING_MSG}") await websocket.send(BitforexWebsocket.PING_MSG) async def _process_message(self, websocket: Websocket, message: str) -> None: if message != BitforexWebsocket.PONG_MSG: message = json.loads(message) subscription_id = BitforexSubscription.make_subscription_id( message['event'], message['param']) await self.publish_message( WebsocketMessage(subscription_id=subscription_id, message=message))
class LiquidWebsocket(WebsocketMgr): WEBSOCKET_URI = "wss://tap.liquid.com/app/LiquidTapClient" def __init__(self, subscriptions: List[Subscription], api_key: str = None, sec_key: str = None, ssl_context = None) -> None: super().__init__(websocket_uri = self.WEBSOCKET_URI, subscriptions = subscriptions, builtin_ping_interval = None, periodic_timeout_sec = 10, ssl_context = ssl_context) self.api_key = api_key self.sec_key = sec_key self.ping_checker = PeriodicChecker(period_ms = 60 * 1000) async def _subscribe(self, websocket: Websocket): authentication_payload = { "token_id": self.api_key, "path": '/realtime', "nonce": int(time.time_ns()) } LOG.debug(f"Authentication payload: {authentication_payload}") signature = jwt.encode(authentication_payload, self.sec_key, 'HS256') authentication_data = { "headers": { "X-Quoine-Auth": signature.decode('utf-8') }, "path": "/realtime" } authentication_request = { "event": "quoine:auth_request", "data": authentication_data } LOG.debug(f"> {authentication_request}") await websocket.send(json.dumps(authentication_request)) async def _process_periodic(self, websocket: Websocket) -> None: if self.ping_checker.check(): ping_message = { "event": "pusher:ping", "data": '' } LOG.debug(f"> {ping_message}") await websocket.send(json.dumps(ping_message)) async def _process_message(self, websocket: Websocket, message: str) -> None: message = json.loads(message) if message['event'] == "pusher:connection_established": pass elif message['event'] == "pusher:pong": pass elif message['event'] == "quoine:auth_success": subscription_messages = [] for subscription in self.subscriptions: subscription_message = subscription.get_subscription_message() subscription_messages.append(subscription_message) LOG.debug(f"> {subscription_messages}") await websocket.send(json.dumps(subscription_messages)) elif message['event'] == "quoine:auth_failure": raise LiquidException(f"Websocket authentication error: {message}") elif message['event'] == "pusher_internal:subscription_succeeded": LOG.info(f'Websocket subscribed successfuly: {message}') elif message['event'] == "updated": await self.publish_message(WebsocketMessage(subscription_id = message['channel'], message = message)) else: raise LiquidException(f"Websocket unknown event type: {message}")
class BtseWebsocket(WebsocketMgr): WEBSOCKET_URI = "wss://ws.btse.com/spotWS" def __init__(self, subscriptions: List[Subscription], api_key: str = None, sec_key: str = None, ssl_context=None) -> None: super().__init__(websocket_uri=self.WEBSOCKET_URI, subscriptions=subscriptions, ssl_context=ssl_context, builtin_ping_interval=None, auto_reconnect=True, periodic_timeout_sec=5) self.api_key = api_key self.sec_key = sec_key self.ping_checker = PeriodicChecker(period_ms=30 * 1000) def get_websocket(self) -> Websocket: return self.get_aiohttp_websocket() async def _process_periodic(self, websocket: Websocket) -> None: if self.ping_checker.check(): await websocket.send("2") async def _authenticate(self, websocket: Websocket): requires_authentication = False for subscription in self.subscriptions: if subscription.requires_authentication(): requires_authentication = True break if requires_authentication: timestamp_ms = int( datetime.datetime.now(tz=datetime.timezone.utc).timestamp() * 1000) signature_string = f"/spotWS{timestamp_ms}" signature = hmac.new(self.sec_key.encode('utf-8'), signature_string.encode('utf-8'), hashlib.sha384).hexdigest() authentication_message = { "op": "authKeyExpires", "args": [self.api_key, timestamp_ms, signature] } LOG.debug(f"> {authentication_message}") await websocket.send(json.dumps(authentication_message)) message = await websocket.receive() LOG.debug(f"< {message}") if 'authenticated successfully' in message: LOG.info(f"Websocket authenticated successfully.") else: raise BtseException( f"Authentication error. Response [{message}]") async def _subscribe(self, websocket: Websocket): subscription_list = [] for subscription in self.subscriptions: subscription_list += subscription.get_subscription_list() subscription_message = {"op": "subscribe", "args": subscription_list} LOG.debug(f"> {subscription_message}") await websocket.send(json.dumps(subscription_message)) async def _process_message(self, websocket: Websocket, message: str) -> None: message = json.loads(message) topic = message['topic'] channel = topic.split(':')[0] await self.publish_message( WebsocketMessage(subscription_id=channel, message=message))