From 2e4f3dcd756d7ba6fcab2dab268c534d9bfe2518 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Fri, 13 Sep 2024 10:37:30 +0200 Subject: [PATCH 1/5] Update manifest.json --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index eea77ee..6a77c95 100644 --- a/manifest.json +++ b/manifest.json @@ -2,7 +2,7 @@ "repos": [ { "id": "nwcprovider", - "organisation": "lnbits", + "organisation": "riccardobl", "repository": "nwcprovider" } ] From 81224f62116c9779f5dd504bb87eddd152338cda Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Tue, 19 Nov 2024 18:26:21 +0000 Subject: [PATCH 2/5] add rate limit --- nwcp.py | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/nwcp.py b/nwcp.py index 09dfcf2..6f6e296 100644 --- a/nwcp.py +++ b/nwcp.py @@ -86,7 +86,8 @@ class NWCServiceProvider: # Subscription self.sub = None - + self.rate_limit = {} + # websocket connection self.ws = None @@ -199,6 +200,29 @@ class NWCServiceProvider: logger.debug("Waiting for connection...") await asyncio.sleep(1) + async def _ratelimit(self, unit, max_sleep_time = 120): + rate_limit = self.rate_limit.get(unit) + if not rate_limit: + self.rate_limit[unit] = rate_limit = { + "backoff": 0, + "last_attempt_time": 0 + } + + if time.time() - rate_limit["last_attempt_time"] > max_sleep_time: + # reset backoff if action lasted more than max_sleep_time + rate_limit["backoff"] = 0 + else: + # increase backoff + rate_limit["backoff"] = ( + min(rate_limit["backoff"] * 2, max_sleep_time) + if rate_limit["backoff"] > 0 + else 1 + ) + logger.debug( + "Sleeping for " + str(rate_limit["backoff"]) + " seconds before " + unit) + await asyncio.sleep( rate_limit["backoff"]) + rate_limit["last_attempt_time"] = time.time() + async def _subscribe(self): """ [Re]Subscribe to receive nip 47 requests and responses from the relay @@ -383,6 +407,7 @@ class NWCServiceProvider: + info + " ... resubscribing..." ) + await self._ratelimit("subscribing") await self._subscribe() async def _on_message(self, ws, message: str): @@ -403,7 +428,7 @@ class NWCServiceProvider: elif msg[0] == "OK": pass else: - raise Exception("Unknown message type") + raise Exception("Unknown message type " + str(msg[0])) except Exception as e: logger.error("Error parsing event: " + str(e)) @@ -443,8 +468,8 @@ class NWCServiceProvider: self.connected = False if not self._is_shutting_down(): # Wait some time before reconnecting - logger.debug("Reconnecting to NWC relay in 5 seconds...") - await asyncio.sleep(5) + logger.debug("Reconnecting to NWC relay...") + await self._ratelimit("connecting") def _encrypt_content( self, content: str, pubkey_hex: str, iv_seed: Optional[int] = None From f69e79c78984606e4376f1c556d7aa2bd69b0da0 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Tue, 19 Nov 2024 19:13:00 +0000 Subject: [PATCH 3/5] fix spent budget abd expired tokens display --- templates/nwcprovider/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/nwcprovider/index.html b/templates/nwcprovider/index.html index d79df9f..89d5e63 100644 --- a/templates/nwcprovider/index.html +++ b/templates/nwcprovider/index.html @@ -615,7 +615,7 @@ try { const response = await LNbits.api.request( "GET", - "/nwcprovider/api/v1/nwc?includeExpired=true&calculateSpendBudget=true", + "/nwcprovider/api/v1/nwc?include_expired=true&calculate_spent_budget=true", wallet.adminkey, ); this.nwcs = response.data; From 1d8dc8803d10a57c760c9cd75d19ac52c07a6397 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Tue, 19 Nov 2024 19:13:09 +0000 Subject: [PATCH 4/5] debug --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ba70814..118a873 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ node_modules data .vscode package.json -package-lock.json \ No newline at end of file +package-lock.json +dump \ No newline at end of file From 036285b67372b535d1564d8260d3098158c91ced Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Tue, 19 Nov 2024 19:28:46 +0000 Subject: [PATCH 5/5] format and types --- nwcp.py | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/nwcp.py b/nwcp.py index 6f6e296..50f6ae7 100644 --- a/nwcp.py +++ b/nwcp.py @@ -16,6 +16,11 @@ from lnbits.settings import settings from loguru import logger +class RateLimit: + backoff: int = 0 + last_attempt_time: int = 0 + + class MainSubscription: def __init__(self): self.requests_sub_id: Optional[str] = None @@ -86,8 +91,8 @@ class NWCServiceProvider: # Subscription self.sub = None - self.rate_limit = {} - + self.rate_limit: Dict[str, RateLimit] = {} + # websocket connection self.ws = None @@ -200,28 +205,22 @@ class NWCServiceProvider: logger.debug("Waiting for connection...") await asyncio.sleep(1) - async def _ratelimit(self, unit, max_sleep_time = 120): - rate_limit = self.rate_limit.get(unit) - if not rate_limit: - self.rate_limit[unit] = rate_limit = { - "backoff": 0, - "last_attempt_time": 0 - } + async def _ratelimit(self, unit: str, max_sleep_time: int = 120) -> None: + limit: Optional[RateLimit] = self.rate_limit.get(unit) + if not limit: + self.rate_limit[unit] = limit = RateLimit() - if time.time() - rate_limit["last_attempt_time"] > max_sleep_time: + if time.time() - limit.last_attempt_time > max_sleep_time: # reset backoff if action lasted more than max_sleep_time - rate_limit["backoff"] = 0 + limit.backoff = 0 else: # increase backoff - rate_limit["backoff"] = ( - min(rate_limit["backoff"] * 2, max_sleep_time) - if rate_limit["backoff"] > 0 - else 1 + limit.backoff = ( + min(limit.backoff * 2, max_sleep_time) if limit.backoff > 0 else 1 ) - logger.debug( - "Sleeping for " + str(rate_limit["backoff"]) + " seconds before " + unit) - await asyncio.sleep( rate_limit["backoff"]) - rate_limit["last_attempt_time"] = time.time() + logger.debug("Sleeping for " + str(limit.backoff) + " seconds before " + unit) + await asyncio.sleep(limit.backoff) + limit.last_attempt_time = int(time.time()) async def _subscribe(self): """