From cac7e1e3c0959a6f025146fb337bea248064a6be Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 31 Jul 2022 23:46:42 +0100 Subject: [PATCH] Works almost Stream needs work --- .env.example | 8 ++- lnbits/wallets/__init__.py | 1 + lnbits/wallets/cliche.py | 125 +++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 lnbits/wallets/cliche.py diff --git a/.env.example b/.env.example index 6ef60bc1c..b6940a53e 100644 --- a/.env.example +++ b/.env.example @@ -34,13 +34,17 @@ LNBITS_SITE_DESCRIPTION="Some description about your service, will display if ti LNBITS_THEME_OPTIONS="classic, bitcoin, freedom, mint, autumn, monochrome, salvador" # LNBITS_CUSTOM_LOGO="https://lnbits.com/assets/images/logo/logo.svg" -# Choose from LNPayWallet, OpenNodeWallet, LntxbotWallet, +# Choose from LNPayWallet, OpenNodeWallet, LntxbotWallet, ClicheWallet # LndRestWallet, CLightningWallet, LNbitsWallet, SparkWallet, FakeWallet, EclairWallet -LNBITS_BACKEND_WALLET_CLASS=VoidWallet +LNBITS_BACKEND_WALLET_CLASS=ClicheWallet # VoidWallet is just a fallback that works without any actual Lightning capabilities, # just so you can see the UI before dealing with this file. # Set one of these blocks depending on the wallet kind you chose above: + +#ClicheWallet +SPARK_ENDPOINT=ws://127.0.0.1:12000 + # SparkWallet SPARK_URL=http://localhost:9737/rpc SPARK_TOKEN=myaccesstoken diff --git a/lnbits/wallets/__init__.py b/lnbits/wallets/__init__.py index 8a2ca1a54..a0c0356d8 100644 --- a/lnbits/wallets/__init__.py +++ b/lnbits/wallets/__init__.py @@ -3,6 +3,7 @@ from .clightning import CLightningWallet from .eclair import EclairWallet from .fake import FakeWallet +from .cliche import ClicheWallet from .lnbits import LNbitsWallet from .lndrest import LndRestWallet from .lnpay import LNPayWallet diff --git a/lnbits/wallets/cliche.py b/lnbits/wallets/cliche.py new file mode 100644 index 000000000..3962c0feb --- /dev/null +++ b/lnbits/wallets/cliche.py @@ -0,0 +1,125 @@ +import asyncio +import json +from os import getenv +from typing import AsyncGenerator, Dict, Optional + +import httpx +from loguru import logger + +from .base import ( + InvoiceResponse, + PaymentResponse, + PaymentStatus, + StatusResponse, + Wallet, +) +from websocket import create_connection + + +class ClicheWallet(Wallet): + """https://github.com/fiatjaf/cliche""" + + def __init__(self): + self.endpoint = getenv("CLICHE_ENDPOINT") + + async def status(self) -> StatusResponse: + try: + ws = create_connection(self.endpoint) + ws.send("get-info") + r = ws.recv() + except Exception as exc: + return StatusResponse( + f"Failed to connect to {self.endpoint} due to: {exc}", 0 + ) + try: + data = json.loads(r) + except: + return StatusResponse( + f"Failed to connect to {self.endpoint}, got: '{r.text[:200]}...'", 0 + ) + + return StatusResponse(None, data["result"]["wallets"][0]["balance"]) + + async def create_invoice( + self, + amount: int, + memo: Optional[str] = None, + description_hash: Optional[bytes] = None, + ) -> InvoiceResponse: + if description_hash: + ws = create_connection(self.endpoint) + ws.send(f"create-invoice --msatoshi {amount*1000} --description_hash {description_hash}") + r = ws.recv() + else: + ws = create_connection(self.endpoint) + ws.send(f"create-invoice --msatoshi {amount*1000} --description {memo}") + r = ws.recv() + data = json.loads(r) + checking_id = None + payment_request = None + error_message = None + try: + if data["error"]["message"]: + logger.error( + data["error"]["message"] + ) + error_message = data["error"]["message"] + return InvoiceResponse(False, checking_id, payment_request, error_message) + except: + checking_id, payment_request = data["result"]["payment_hash"], data["result"]["invoice"] + return InvoiceResponse(True, checking_id, payment_request, error_message) + + async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse: + + ws = create_connection(self.endpoint) + ws.send(f"pay-invoice --invoice {bolt11}") + r = ws.recv() + data = json.loads(r) + checking_id = None + error_message = None + try: + if data["error"]["message"]: + logger.error( + data["error"]["message"] + ) + error_message = data["error"]["message"] + return PaymentResponse(False, None, 0, error_message) + except: + checking_id = data["result"]["payment_hash"] + return PaymentResponse(True, checking_id, 0, error_message) + + async def get_invoice_status(self, checking_id: str) -> PaymentStatus: + ws = create_connection(self.endpoint) + ws.send(f"check-payment --hash {checking_id}") + r = ws.recv() + data = json.loads(r) + try: + if data["error"]["message"]: + logger.error( + data["error"]["message"] + ) + return PaymentStatus(None) + except: + statuses = {"pending": None, "complete": True, "failed": False} + return PaymentStatus(statuses[data["result"]["status"]]) + + async def get_payment_status(self, checking_id: str) -> PaymentStatus: + ws = create_connection(self.endpoint) + ws.send(f"check-payment --hash {checking_id}") + r = ws.recv() + data = json.loads(r) + try: + if data["error"]["message"]: + logger.error( + data["error"]["message"] + ) + return PaymentStatus(None) + except: + statuses = {"pending": None, "complete": True, "failed": False} + return PaymentStatus(statuses[data["result"]["status"]]) + + async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: + self.queue: asyncio.Queue = asyncio.Queue(0) + while True: + value = await self.queue.get() + yield value \ No newline at end of file