mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2025-02-23 06:35:23 +01:00
Merge pull request #817 from lnbits/clichefund
Adds Cliche as a funding source
This commit is contained in:
commit
28c50d93ce
6 changed files with 172 additions and 4 deletions
|
@ -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
|
||||
# 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
|
||||
CLICHE_ENDPOINT=ws://127.0.0.1:12000
|
||||
|
||||
# SparkWallet
|
||||
SPARK_URL=http://localhost:9737/rpc
|
||||
SPARK_TOKEN=myaccesstoken
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# flake8: noqa
|
||||
|
||||
from .cliche import ClicheWallet
|
||||
from .clightning import CLightningWallet
|
||||
from .eclair import EclairWallet
|
||||
from .fake import FakeWallet
|
||||
|
|
141
lnbits/wallets/cliche.py
Normal file
141
lnbits/wallets/cliche.py
Normal file
|
@ -0,0 +1,141 @@
|
|||
import asyncio
|
||||
import json
|
||||
from os import getenv
|
||||
from typing import AsyncGenerator, Dict, Optional
|
||||
|
||||
import httpx
|
||||
from loguru import logger
|
||||
from websocket import create_connection
|
||||
|
||||
from .base import (
|
||||
InvoiceResponse,
|
||||
PaymentResponse,
|
||||
PaymentStatus,
|
||||
StatusResponse,
|
||||
Wallet,
|
||||
)
|
||||
|
||||
|
||||
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.hex()}"
|
||||
)
|
||||
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
|
||||
|
||||
if data.get("error") is not None and data["error"].get("message"):
|
||||
logger.error(data["error"]["message"])
|
||||
error_message = data["error"]["message"]
|
||||
return InvoiceResponse(False, checking_id, payment_request, error_message)
|
||||
|
||||
if data.get("result") is not None:
|
||||
checking_id, payment_request = (
|
||||
data["result"]["payment_hash"],
|
||||
data["result"]["invoice"],
|
||||
)
|
||||
else:
|
||||
return InvoiceResponse(
|
||||
False, checking_id, payment_request, "Could not get payment hash"
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
if data.get("error") is not None and data["error"].get("message"):
|
||||
logger.error(data["error"]["message"])
|
||||
error_message = data["error"]["message"]
|
||||
return PaymentResponse(False, None, 0, error_message)
|
||||
|
||||
if data.get("result") is not None and data["result"].get("payment_hash"):
|
||||
checking_id = data["result"]["payment_hash"]
|
||||
else:
|
||||
return PaymentResponse(False, checking_id, 0, "Could not get 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)
|
||||
|
||||
if data.get("error") is not None and data["error"].get("message"):
|
||||
logger.error(data["error"]["message"])
|
||||
return PaymentStatus(None)
|
||||
|
||||
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)
|
||||
|
||||
if data.get("error") is not None and data["error"].get("message"):
|
||||
logger.error(data["error"]["message"])
|
||||
return PaymentStatus(None)
|
||||
|
||||
statuses = {"pending": None, "complete": True, "failed": False}
|
||||
return PaymentStatus(statuses[data["result"]["status"]])
|
||||
|
||||
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
|
||||
try:
|
||||
ws = await create_connection(self.endpoint)
|
||||
while True:
|
||||
r = await ws.recv()
|
||||
data = json.loads(r)
|
||||
try:
|
||||
if data["result"]["status"]:
|
||||
yield data["result"]["payment_hash"]
|
||||
except:
|
||||
continue
|
||||
except:
|
||||
pass
|
||||
logger.error("lost connection to cliche's websocket, retrying in 5 seconds")
|
||||
await asyncio.sleep(5)
|
26
poetry.lock
generated
26
poetry.lock
generated
|
@ -588,6 +588,19 @@ category = "main"
|
|||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "websocket-client"
|
||||
version = "1.3.3"
|
||||
description = "WebSocket client for Python with low level API options"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.extras]
|
||||
docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"]
|
||||
optional = ["python-socks", "wsaccel"]
|
||||
test = ["websockets"]
|
||||
|
||||
[[package]]
|
||||
name = "websockets"
|
||||
version = "10.0"
|
||||
|
@ -605,7 +618,7 @@ optional = false
|
|||
python-versions = ">=3.5"
|
||||
|
||||
[package.extras]
|
||||
dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"]
|
||||
dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "zipp"
|
||||
|
@ -622,7 +635,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
|
|||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.9"
|
||||
content-hash = "921a5f4fe1a4d1a4c3b490f8631ed4bdd0d8af1f1992f1a4f74eaed986c4eb0b"
|
||||
content-hash = "f1d88c59b67ee4198292f28ddd2caa33249b4dee85eda0c75c1c2100a583c55e"
|
||||
|
||||
[metadata.files]
|
||||
aiofiles = [
|
||||
|
@ -1131,6 +1144,10 @@ watchgod = [
|
|||
{file = "watchgod-0.7-py3-none-any.whl", hash = "sha256:d6c1ea21df37847ac0537ca0d6c2f4cdf513562e95f77bb93abbcf05573407b7"},
|
||||
{file = "watchgod-0.7.tar.gz", hash = "sha256:48140d62b0ebe9dd9cf8381337f06351e1f2e70b2203fa9c6eff4e572ca84f29"},
|
||||
]
|
||||
websocket-client = [
|
||||
{file = "websocket-client-1.3.3.tar.gz", hash = "sha256:d58c5f284d6a9bf8379dab423259fe8f85b70d5fa5d2916d5791a84594b122b1"},
|
||||
{file = "websocket_client-1.3.3-py3-none-any.whl", hash = "sha256:5d55652dc1d0b3c734f044337d929aaf83f4f9138816ec680c1aefefb4dc4877"},
|
||||
]
|
||||
websockets = [
|
||||
{file = "websockets-10.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cd8c6f2ec24aedace251017bc7a414525171d4e6578f914acab9349362def4da"},
|
||||
{file = "websockets-10.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1f6b814cff6aadc4288297cb3a248614829c6e4ff5556593c44a115e9dd49939"},
|
||||
|
@ -1158,7 +1175,10 @@ websockets = [
|
|||
{file = "websockets-10.0-cp39-cp39-win_amd64.whl", hash = "sha256:c5880442f5fc268f1ef6d37b2c152c114deccca73f48e3a8c48004d2f16f4567"},
|
||||
{file = "websockets-10.0.tar.gz", hash = "sha256:c4fc9a1d242317892590abe5b61a9127f1a61740477bfb121743f290b8054002"},
|
||||
]
|
||||
win32-setctime = []
|
||||
win32-setctime = [
|
||||
{file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"},
|
||||
{file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
|
||||
]
|
||||
zipp = [
|
||||
{file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"},
|
||||
{file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"},
|
||||
|
|
|
@ -59,6 +59,7 @@ websockets = "10.0"
|
|||
zipp = "3.5.0"
|
||||
loguru = "0.5.3"
|
||||
cffi = "1.15.0"
|
||||
websocket-client = "^1.3.3"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
|
||||
|
|
|
@ -50,3 +50,4 @@ uvicorn==0.18.2
|
|||
uvloop==0.16.0
|
||||
watchfiles==0.16.0
|
||||
websockets==10.3
|
||||
websocket-client==1.3.3
|
||||
|
|
Loading…
Add table
Reference in a new issue