mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2025-02-23 22:47:05 +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_THEME_OPTIONS="classic, bitcoin, freedom, mint, autumn, monochrome, salvador"
|
||||||
# LNBITS_CUSTOM_LOGO="https://lnbits.com/assets/images/logo/logo.svg"
|
# 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
|
# LndRestWallet, CLightningWallet, LNbitsWallet, SparkWallet, FakeWallet, EclairWallet
|
||||||
LNBITS_BACKEND_WALLET_CLASS=VoidWallet
|
LNBITS_BACKEND_WALLET_CLASS=VoidWallet
|
||||||
# VoidWallet is just a fallback that works without any actual Lightning capabilities,
|
# VoidWallet is just a fallback that works without any actual Lightning capabilities,
|
||||||
# just so you can see the UI before dealing with this file.
|
# 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:
|
# Set one of these blocks depending on the wallet kind you chose above:
|
||||||
|
|
||||||
|
# ClicheWallet
|
||||||
|
CLICHE_ENDPOINT=ws://127.0.0.1:12000
|
||||||
|
|
||||||
# SparkWallet
|
# SparkWallet
|
||||||
SPARK_URL=http://localhost:9737/rpc
|
SPARK_URL=http://localhost:9737/rpc
|
||||||
SPARK_TOKEN=myaccesstoken
|
SPARK_TOKEN=myaccesstoken
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# flake8: noqa
|
# flake8: noqa
|
||||||
|
|
||||||
|
from .cliche import ClicheWallet
|
||||||
from .clightning import CLightningWallet
|
from .clightning import CLightningWallet
|
||||||
from .eclair import EclairWallet
|
from .eclair import EclairWallet
|
||||||
from .fake import FakeWallet
|
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
|
optional = false
|
||||||
python-versions = ">=3.5"
|
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]]
|
[[package]]
|
||||||
name = "websockets"
|
name = "websockets"
|
||||||
version = "10.0"
|
version = "10.0"
|
||||||
|
@ -605,7 +618,7 @@ optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.5"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"]
|
dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zipp"
|
name = "zipp"
|
||||||
|
@ -622,7 +635,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.9"
|
python-versions = "^3.9"
|
||||||
content-hash = "921a5f4fe1a4d1a4c3b490f8631ed4bdd0d8af1f1992f1a4f74eaed986c4eb0b"
|
content-hash = "f1d88c59b67ee4198292f28ddd2caa33249b4dee85eda0c75c1c2100a583c55e"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
aiofiles = [
|
aiofiles = [
|
||||||
|
@ -1131,6 +1144,10 @@ watchgod = [
|
||||||
{file = "watchgod-0.7-py3-none-any.whl", hash = "sha256:d6c1ea21df37847ac0537ca0d6c2f4cdf513562e95f77bb93abbcf05573407b7"},
|
{file = "watchgod-0.7-py3-none-any.whl", hash = "sha256:d6c1ea21df37847ac0537ca0d6c2f4cdf513562e95f77bb93abbcf05573407b7"},
|
||||||
{file = "watchgod-0.7.tar.gz", hash = "sha256:48140d62b0ebe9dd9cf8381337f06351e1f2e70b2203fa9c6eff4e572ca84f29"},
|
{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 = [
|
websockets = [
|
||||||
{file = "websockets-10.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cd8c6f2ec24aedace251017bc7a414525171d4e6578f914acab9349362def4da"},
|
{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"},
|
{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-cp39-cp39-win_amd64.whl", hash = "sha256:c5880442f5fc268f1ef6d37b2c152c114deccca73f48e3a8c48004d2f16f4567"},
|
||||||
{file = "websockets-10.0.tar.gz", hash = "sha256:c4fc9a1d242317892590abe5b61a9127f1a61740477bfb121743f290b8054002"},
|
{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 = [
|
zipp = [
|
||||||
{file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"},
|
{file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"},
|
||||||
{file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"},
|
{file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"},
|
||||||
|
|
|
@ -59,6 +59,7 @@ websockets = "10.0"
|
||||||
zipp = "3.5.0"
|
zipp = "3.5.0"
|
||||||
loguru = "0.5.3"
|
loguru = "0.5.3"
|
||||||
cffi = "1.15.0"
|
cffi = "1.15.0"
|
||||||
|
websocket-client = "^1.3.3"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
|
|
||||||
|
|
|
@ -50,3 +50,4 @@ uvicorn==0.18.2
|
||||||
uvloop==0.16.0
|
uvloop==0.16.0
|
||||||
watchfiles==0.16.0
|
watchfiles==0.16.0
|
||||||
websockets==10.3
|
websockets==10.3
|
||||||
|
websocket-client==1.3.3
|
||||||
|
|
Loading…
Add table
Reference in a new issue