Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update to lnbits 1.0.0 #53

Merged
merged 19 commits into from
Nov 12, 2024
2 changes: 1 addition & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "Withdraw Links",
"short_description": "Make LNURL withdraw links",
"tile": "/withdraw/static/image/lnurl-withdraw.png",
"min_lnbits_version": "0.12.11",
"min_lnbits_version": "1.0.0",
"contributors": [
{
"name": "arcbtc",
Expand Down
194 changes: 77 additions & 117 deletions crud.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from datetime import datetime
from time import time
from typing import List, Optional, Tuple
from typing import Optional

import shortuuid
from lnbits.db import Database
Expand All @@ -16,107 +15,78 @@ async def create_withdraw_link(
) -> WithdrawLink:
link_id = urlsafe_short_hash()[:22]
available_links = ",".join([str(i) for i in range(data.uses)])
await db.execute(
f"""
INSERT INTO withdraw.withdraw_link (
id,
wallet,
title,
min_withdrawable,
max_withdrawable,
uses,
wait_time,
is_unique,
unique_hash,
k1,
open_time,
usescsv,
webhook_url,
webhook_headers,
webhook_body,
custom_url,
created_at
)
VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, {db.timestamp_placeholder})
""",
(
link_id,
wallet_id,
data.title,
data.min_withdrawable,
data.max_withdrawable,
data.uses,
data.wait_time,
int(data.is_unique),
urlsafe_short_hash(),
urlsafe_short_hash(),
int(datetime.now().timestamp()) + data.wait_time,
available_links,
data.webhook_url,
data.webhook_headers,
data.webhook_body,
data.custom_url,
int(time()),
),
withdraw_link = WithdrawLink(
id=link_id,
wallet=wallet_id,
unique_hash=urlsafe_short_hash(),
k1=urlsafe_short_hash(),
created_at=datetime.now(),
open_time=int(datetime.now().timestamp()) + data.wait_time,
title=data.title,
min_withdrawable=data.min_withdrawable,
max_withdrawable=data.max_withdrawable,
uses=data.uses,
wait_time=data.wait_time,
is_unique=data.is_unique,
usescsv=available_links,
webhook_url=data.webhook_url,
webhook_headers=data.webhook_headers,
webhook_body=data.webhook_body,
custom_url=data.custom_url,
number=0,
)
link = await get_withdraw_link(link_id, 0)
assert link, "Newly created link couldn't be retrieved"
return link
await db.insert("withdraw.withdraw_link", withdraw_link)
return withdraw_link


async def get_withdraw_link(link_id: str, num=0) -> Optional[WithdrawLink]:
row = await db.fetchone(
"SELECT * FROM withdraw.withdraw_link WHERE id = ?", (link_id,)
link = await db.fetchone(
"SELECT * FROM withdraw.withdraw_link WHERE id = :id",
{"id": link_id},
WithdrawLink,
)
if not row:
if not link:
return None

link = dict(**row)
link["number"] = num

return WithdrawLink.parse_obj(link)
link.number = num
return link


async def get_withdraw_link_by_hash(unique_hash: str, num=0) -> Optional[WithdrawLink]:
row = await db.fetchone(
"SELECT * FROM withdraw.withdraw_link WHERE unique_hash = ?", (unique_hash,)
link = await db.fetchone(
"SELECT * FROM withdraw.withdraw_link WHERE unique_hash = :hash",
{"hash": unique_hash},
WithdrawLink,
)
if not row:
if not link:
return None

link = dict(**row)
link["number"] = num

return WithdrawLink.parse_obj(link)
link.number = num
return link


async def get_withdraw_links(
wallet_ids: List[str], limit: int, offset: int
) -> Tuple[List[WithdrawLink], int]:
rows = await db.fetchall(
"""
SELECT * FROM withdraw.withdraw_link
WHERE wallet IN ({})
ORDER BY open_time DESC
LIMIT ? OFFSET ?
""".format(
",".join("?" * len(wallet_ids))
),
(*wallet_ids, limit, offset),
wallet_ids: list[str], limit: int, offset: int
) -> tuple[list[WithdrawLink], int]:
q = ",".join([f"'{w}'" for w in wallet_ids])
links = await db.fetchall(
f"""
SELECT * FROM withdraw.withdraw_link WHERE wallet IN ({q})
ORDER BY open_time DESC LIMIT :limit OFFSET :offset
""",
{"limit": limit, "offset": offset},
WithdrawLink,
)

total = await db.fetchone(
"""
result = await db.execute(
f"""
SELECT COUNT(*) as total FROM withdraw.withdraw_link
WHERE wallet IN ({})
""".format(
",".join("?" * len(wallet_ids))
),
(*wallet_ids,),
WHERE wallet IN ({q})
"""
)
total = result.mappings().first()

return [WithdrawLink(**row) for row in rows], total["total"]
return links, total.total


async def remove_unique_withdraw_link(link: WithdrawLink, unique_hash: str) -> None:
Expand All @@ -125,36 +95,25 @@ async def remove_unique_withdraw_link(link: WithdrawLink, unique_hash: str) -> N
for x in link.usescsv.split(",")
if unique_hash != shortuuid.uuid(name=link.id + link.unique_hash + x.strip())
]
await update_withdraw_link(
link.id,
usescsv=",".join(unique_links),
)
link.usescsv = ",".join(unique_links)
await update_withdraw_link(link)


async def increment_withdraw_link(link: WithdrawLink) -> None:
await update_withdraw_link(
link.id,
used=link.used + 1,
open_time=link.wait_time + int(datetime.now().timestamp()),
)
link.used = link.used + 1
link.open_time = int(datetime.now().timestamp()) + link.wait_time
await update_withdraw_link(link)


async def update_withdraw_link(link_id: str, **kwargs) -> Optional[WithdrawLink]:
if "is_unique" in kwargs:
kwargs["is_unique"] = int(kwargs["is_unique"])
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
await db.execute(
f"UPDATE withdraw.withdraw_link SET {q} WHERE id = ?",
(*kwargs.values(), link_id),
)
row = await db.fetchone(
"SELECT * FROM withdraw.withdraw_link WHERE id = ?", (link_id,)
)
return WithdrawLink(**row) if row else None
async def update_withdraw_link(link: WithdrawLink) -> WithdrawLink:
await db.update("withdraw.withdraw_link", link)
return link


async def delete_withdraw_link(link_id: str) -> None:
await db.execute("DELETE FROM withdraw.withdraw_link WHERE id = ?", (link_id,))
await db.execute(
"DELETE FROM withdraw.withdraw_link WHERE id = :id", {"id": link_id}
)


def chunks(lst, n):
Expand All @@ -165,35 +124,36 @@ def chunks(lst, n):
async def create_hash_check(the_hash: str, lnurl_id: str) -> HashCheck:
await db.execute(
"""
INSERT INTO withdraw.hash_check (
id,
lnurl_id
)
VALUES (?, ?)
INSERT INTO withdraw.hash_check (id, lnurl_id)
VALUES (:id, :lnurl_id)
""",
(the_hash, lnurl_id),
{"id": the_hash, "lnurl_id": lnurl_id},
)
hash_check = await get_hash_check(the_hash, lnurl_id)
return hash_check


async def get_hash_check(the_hash: str, lnurl_id: str) -> HashCheck:
rowid = await db.fetchone(
"SELECT * FROM withdraw.hash_check WHERE id = ?", (the_hash,)
hash_check = await db.fetchone(
"SELECT * FROM withdraw.hash_check WHERE id = :id", {"id": the_hash}, HashCheck
)
rowlnurl = await db.fetchone(
"SELECT * FROM withdraw.hash_check WHERE lnurl_id = ?", (lnurl_id,)
hash_check_lnurl = await db.fetchone(
"SELECT * FROM withdraw.hash_check WHERE lnurl_id = :id",
{"id": lnurl_id},
HashCheck,
)
if not rowlnurl:
if not hash_check_lnurl:
await create_hash_check(the_hash, lnurl_id)
return HashCheck(lnurl=True, hash=False)
else:
if not rowid:
if not hash_check:
await create_hash_check(the_hash, lnurl_id)
return HashCheck(lnurl=True, hash=False)
else:
return HashCheck(lnurl=True, hash=True)


async def delete_hash_check(the_hash: str) -> None:
await db.execute("DELETE FROM withdraw.hash_check WHERE id = ?", (the_hash,))
await db.execute(
"DELETE FROM withdraw.hash_check WHERE id = :hash", {"hash": the_hash}
)
10 changes: 0 additions & 10 deletions migrations.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
from time import time


async def m001_initial(db):
"""
Creates an improved withdraw table and migrates the existing data.
Expand Down Expand Up @@ -142,10 +139,3 @@ async def m007_add_created_at_timestamp(db):
"ALTER TABLE withdraw.withdraw_link "
f"ADD COLUMN created_at TIMESTAMP DEFAULT {db.timestamp_column_default}"
)
# Set created_at to current time for all existing rows
await db.execute(
f"""
UPDATE withdraw.withdraw_link SET created_at = {db.timestamp_placeholder}
""",
(int(time()),),
)
16 changes: 7 additions & 9 deletions models.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import datetime
from datetime import datetime

import shortuuid
from fastapi import Query, Request

# TODO remove type: ignore when 0.12.11 is released
from lnurl import ( # type: ignore
ClearnetUrl, # type: ignore
from lnurl import (
ClearnetUrl,
Lnurl,
LnurlWithdrawResponse,
MilliSatoshi, # type: ignore
MilliSatoshi,
)
from lnurl import encode as lnurl_encode
from pydantic import BaseModel
from pydantic import BaseModel, Field


class CreateWithdrawData(BaseModel):
Expand All @@ -29,7 +27,6 @@ class CreateWithdrawData(BaseModel):

class WithdrawLink(BaseModel):
id: str
created_at: datetime.datetime
wallet: str = Query(None)
title: str = Query(None)
min_withdrawable: int = Query(0)
Expand All @@ -42,11 +39,12 @@ class WithdrawLink(BaseModel):
open_time: int = Query(0)
used: int = Query(0)
usescsv: str = Query(None)
number: int = Query(0)
number: int = Field(default=0, no_database=True)
webhook_url: str = Query(None)
webhook_headers: str = Query(None)
webhook_body: str = Query(None)
custom_url: str = Query(None)
created_at: datetime

@property
def is_spent(self) -> bool:
Expand Down
Loading