-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b8482a9
Showing
25 changed files
with
1,785 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2023 Pieter Meulenhoff | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# BitcoinTaps - <small>[lnbits](https://github.com/lnbits/lnbits) extension</small> | ||
<small>For BitcoinTaps devices</small> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import asyncio | ||
from typing import List | ||
|
||
from fastapi import APIRouter | ||
from fastapi.staticfiles import StaticFiles | ||
|
||
from lnbits.db import Database | ||
from lnbits.helpers import template_renderer | ||
from lnbits.tasks import catch_everything_and_restart | ||
|
||
|
||
|
||
db = Database("ext_partytap") | ||
|
||
partytap_ext: APIRouter = APIRouter(prefix="/partytap", tags=["partytap"]) | ||
|
||
scheduled_tasks: List[asyncio.Task] = [] | ||
|
||
partytap_static_files = [ | ||
{ | ||
"path": "/partytap/static", | ||
"app": StaticFiles(directory="lnbits/extensions/partytap/static"), | ||
"name": "partytap_static", | ||
} | ||
] | ||
|
||
|
||
def partytap_renderer(): | ||
return template_renderer(["lnbits/extensions/partytap/templates"]) | ||
|
||
|
||
from .lnurl import * # noqa: F401,F403 | ||
from .tasks import wait_for_paid_invoices | ||
from .views import * # noqa: F401,F403 | ||
from .views_api import * # noqa: F401,F403 | ||
|
||
|
||
def partytap_start(): | ||
loop = asyncio.get_event_loop() | ||
task = loop.create_task(catch_everything_and_restart(wait_for_paid_invoices)) | ||
scheduled_tasks.append(task) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"name": "BitcoinTaps PartyTap", | ||
"short_description": "Enables Bitcoin Lightning enabled (Beer)taps", | ||
"tile": "/partytap/static/image/bitcointaps.png", | ||
"contributors": ["pieterjm"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
import json | ||
from typing import List, Optional | ||
|
||
import shortuuid | ||
from fastapi import Request | ||
from lnurl import encode as lnurl_encode | ||
|
||
from lnbits.helpers import urlsafe_short_hash | ||
|
||
from . import db | ||
from .models import CreateLnurldevice, Lnurldevice, LnurldevicePayment | ||
from loguru import logger | ||
|
||
|
||
|
||
async def create_device(data: CreateLnurldevice, req: Request) -> Lnurldevice: | ||
logger.debug("create_device") | ||
device_id = shortuuid.uuid()[:8] | ||
device_key = urlsafe_short_hash() | ||
|
||
if data.switches: | ||
url = str(req.url_for("partytap.lnurl_v2_params", device_id=device_id)) | ||
for _switch in data.switches: | ||
_switch.id = shortuuid.uuid()[:8] | ||
_switch.lnurl = lnurl_encode( | ||
url | ||
+ "?switch_id=" | ||
+ str(_switch.id) | ||
) | ||
|
||
await db.execute( | ||
"INSERT INTO partytap.device (id, key, title, wallet, currency, switches) VALUES (?, ?, ?, ?, ?, ?)", | ||
( | ||
device_id, | ||
device_key, | ||
data.title, | ||
data.wallet, | ||
data.currency, | ||
json.dumps(data.switches, default=lambda x: x.dict()), | ||
), | ||
) | ||
|
||
device = await get_device(device_id) | ||
assert device, "Lnurldevice was created but could not be retrieved" | ||
return device | ||
|
||
|
||
async def update_device( | ||
device_id: str, data: CreateLnurldevice, req: Request | ||
) -> Lnurldevice: | ||
|
||
if data.switches: | ||
url = str(req.url_for("partytap.lnurl_v2_params", device_id=device_id)) | ||
for _switch in data.switches: | ||
if _switch.id is None: | ||
_switch.id = shortuuid.uuid()[:8] | ||
_switch.lnurl = lnurl_encode( | ||
url | ||
+ "?switch_id=" | ||
+ str(_switch.id) | ||
) | ||
|
||
await db.execute( | ||
""" | ||
UPDATE partytap.device SET | ||
title = ?, | ||
wallet = ?, | ||
currency = ?, | ||
switches = ? | ||
WHERE id = ? | ||
""", | ||
( | ||
data.title, | ||
data.wallet, | ||
data.currency, | ||
json.dumps(data.switches, default=lambda x: x.dict()), | ||
device_id, | ||
), | ||
) | ||
device = await get_device(device_id) | ||
assert device, "Lnurldevice was updated but could not be retrieved" | ||
return device | ||
|
||
async def get_device(device_id: str) -> Optional[Lnurldevice]: | ||
row = await db.fetchone( | ||
"SELECT * FROM partytap.device WHERE id = ?", (device_id,) | ||
) | ||
if not row: | ||
return None | ||
device = Lnurldevice(**row) | ||
|
||
return device | ||
|
||
|
||
async def get_devices(wallet_ids: List[str]) -> List[Lnurldevice]: | ||
|
||
q = ",".join(["?"] * len(wallet_ids)) | ||
rows = await db.fetchall( | ||
f""" | ||
SELECT * FROM partytap.device WHERE wallet IN ({q}) | ||
ORDER BY id | ||
""", | ||
(*wallet_ids,), | ||
) | ||
|
||
# this is needed for backwards compabtibility, before the LNURL were cached inside db | ||
devices = [Lnurldevice(**row) for row in rows] | ||
|
||
return devices | ||
|
||
|
||
async def delete_device(lnurldevice_id: str) -> None: | ||
await db.execute( | ||
"DELETE FROM partytap.device WHERE id = ?", (lnurldevice_id,) | ||
) | ||
|
||
|
||
async def create_payment( | ||
device_id: str, | ||
switch_id: str, | ||
payload: Optional[str] = None, | ||
payhash: Optional[str] = None, | ||
sats: Optional[int] = 0, | ||
) -> LnurldevicePayment: | ||
|
||
|
||
payment_id = urlsafe_short_hash() | ||
await db.execute( | ||
""" | ||
INSERT INTO partytap.payment ( | ||
id, | ||
deviceid, | ||
switchid, | ||
payload, | ||
payhash, | ||
sats | ||
) | ||
VALUES (?, ?, ?, ?, ?, ?) | ||
""", | ||
(payment_id, device_id, switch_id, payload, payhash, sats), | ||
) | ||
payment = await get_payment(payment_id) | ||
assert payment, "Couldnt retrieve newly created payment" | ||
return payment | ||
|
||
|
||
async def update_payment( | ||
payment_id: str, **kwargs | ||
) -> LnurldevicePayment: | ||
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) | ||
await db.execute( | ||
f"UPDATE partytap.payment SET {q} WHERE id = ?", | ||
(*kwargs.values(), payment_id), | ||
) | ||
dpayment = await get_payment(payment_id) | ||
assert dpayment, "Couldnt retrieve update LnurldevicePayment" | ||
return dpayment | ||
|
||
|
||
async def get_payment( | ||
lnurldevicepayment_id: str, | ||
) -> Optional[LnurldevicePayment]: | ||
row = await db.fetchone( | ||
"SELECT * FROM partytap.payment WHERE id = ?", | ||
(lnurldevicepayment_id,), | ||
) | ||
return LnurldevicePayment(**row) if row else None | ||
|
||
async def get_payment_by_p( | ||
p: str, | ||
) -> Optional[LnurldevicePayment]: | ||
row = await db.fetchone( | ||
"SELECT * FROM partytap.payment WHERE payhash = ?", | ||
(p,), | ||
) | ||
return LnurldevicePayment(**row) if row else None | ||
|
||
async def get_lnurlpayload( | ||
lnurldevicepayment_payload: str, | ||
) -> Optional[LnurldevicePayment]: | ||
row = await db.fetchone( | ||
"SELECT * FROM partytap.payment WHERE payload = ?", | ||
(lnurldevicepayment_payload,), | ||
) | ||
return LnurldevicePayment(**row) if row else None | ||
|
||
|
||
def create_payment_memo(device, switch) -> str: | ||
memo = "" | ||
if device.title: | ||
memo += device.title | ||
if switch.label: | ||
memo += " " | ||
memo += switch.label | ||
return memo | ||
|
||
def create_payment_metadata(device, switch): | ||
return json.dumps([["text/plain", create_payment_memo(device,switch)]]) | ||
|
||
|
||
|
Oops, something went wrong.