diff --git a/bfxapi/client.py b/bfxapi/client.py index 7374251..cefd5ce 100644 --- a/bfxapi/client.py +++ b/bfxapi/client.py @@ -7,5 +7,5 @@ class Constants(str, Enum): PUB_WSS_HOST = "wss://api-pub.bitfinex.com/ws/2" class Client(object): - def __init__(self, WSS_HOST: str = Constants.WSS_HOST): - self.wss = BfxWebsocketClient(host=WSS_HOST) \ No newline at end of file + def __init__(self, WSS_HOST: str = Constants.WSS_HOST, API_KEY: str = None, API_SECRET: str = None): + self.wss = BfxWebsocketClient(host=WSS_HOST, API_KEY=API_KEY, API_SECRET=API_SECRET) \ No newline at end of file diff --git a/bfxapi/websocket/BfxWebsocketClient.py b/bfxapi/websocket/BfxWebsocketClient.py index dad9ec1..3b8ee70 100644 --- a/bfxapi/websocket/BfxWebsocketClient.py +++ b/bfxapi/websocket/BfxWebsocketClient.py @@ -1,30 +1,31 @@ -import json, asyncio, websockets +import json, asyncio, hmac, hashlib, time, websockets from pyee.asyncio import AsyncIOEventEmitter from .manager import Manager -from .channels import Channels -from .errors import ConnectionNotOpen + +from .errors import ConnectionNotOpen, AuthenticationCredentialsError class BfxWebsocketClient(object): - def __init__(self, host, channels=None): + def __init__(self, host, API_KEY=None, API_SECRET=None): self.host, self.chanIds, self.event_emitter = host, dict(), AsyncIOEventEmitter() self.manager, self.websocket = Manager(event_emitter=self.event_emitter), None - self.channels = channels or list() + self.API_KEY, self.API_SECRET = API_KEY, API_SECRET def run_forever(self): asyncio.run(self.connect()) async def connect(self): async for websocket in websockets.connect(self.host): - try: - self.websocket = websocket + self.websocket = websocket - for channel, parameters in self.channels: - await self.subscribe(channel, **parameters) - else: self.event_emitter.emit("open") + try: + self.event_emitter.emit("open") + + if self.API_KEY != None and self.API_SECRET != None: + self.authenticate(self.API_KEY, self.API_SECRET) async for message in websocket: message = json.loads(message) @@ -38,12 +39,21 @@ class BfxWebsocketClient(object): if message["status"] == "OK": del self.chanIds[message["chanId"]] + elif isinstance(message, dict) and message["event"] == "auth": + if message["status"] == "OK": + self.chanIds[message["chanId"]] = message + + self.event_emitter.emit("authenticated", message) + else: raise AuthenticationCredentialsError("Cannot authenticate with given API-KEY and API-SECRET.") + elif isinstance(message, list): chanId, parameters = message[0], message[1:] + self.manager.handle(self.chanIds[chanId], *parameters) except websockets.ConnectionClosed: continue + @staticmethod def __require_websocket_connection(function): async def wrapper(self, *args, **kwargs): if self.websocket == None or self.websocket.open == False: @@ -68,6 +78,22 @@ class BfxWebsocketClient(object): "chanId": chanId })) + @__require_websocket_connection + async def authenticate(self, API_KEY, API_SECRET, filter=None): + data = { "event": "auth", "filter": filter, "apiKey": API_KEY } + + data["authNonce"] = int(time.time()) * 1000 + + data["authPayload"] = "AUTH" + str(data["authNonce"]) + + data["authSig"] = hmac.new( + API_SECRET.encode("utf8"), + data["authPayload"].encode("utf8"), + hashlib.sha384 + ).hexdigest() + + await self.websocket.send(json.dumps(data)) + async def clear(self): for chanId in self.chanIds.keys(): await self.unsubscribe(chanId) @@ -76,4 +102,10 @@ class BfxWebsocketClient(object): def handler(function): self.event_emitter.on(event, function) + return handler + + def once(self, event): + def handler(function): + self.event_emitter.once(event, function) + return handler \ No newline at end of file diff --git a/bfxapi/websocket/errors.py b/bfxapi/websocket/errors.py index d049ac7..693dbe3 100644 --- a/bfxapi/websocket/errors.py +++ b/bfxapi/websocket/errors.py @@ -1,7 +1,13 @@ class ConnectionNotOpen(Exception): """ This error indicates an attempt to communicate via websocket before starting the connection with the servers. - + """ + + pass + +class AuthenticationCredentialsError(Exception): + """ + This error indicates that the user has provided incorrect credentials (API-KEY and API-SECRET) for authentication. """ pass \ No newline at end of file