Skip to content

Commit

Permalink
feat: update to lnbits 1.0.0 (#15)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: Vlad Stan <[email protected]>
  • Loading branch information
dni and motorina0 authored Oct 24, 2024
1 parent 57e107f commit d328213
Show file tree
Hide file tree
Showing 12 changed files with 1,598 additions and 1,485 deletions.
2 changes: 1 addition & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
"short_description": "Accept Bitcoin donations, with messages attached!",
"tile": "/tipjar/static/image/tipjar.png",
"contributors": ["Fittiboy"],
"min_lnbits_version": "0.11.0"
"min_lnbits_version": "1.0.0"
}
121 changes: 44 additions & 77 deletions crud.py
Original file line number Diff line number Diff line change
@@ -1,128 +1,95 @@
from typing import Optional

from lnbits.db import SQLITE, Database
from lnbits.db import Database
from lnbits.helpers import urlsafe_short_hash

from .models import CreateTipJar, Tip, TipJar

db = Database("ext_tipjar")


async def create_tip(
tip_id: str, wallet: str, message: str, name: str, sats: int, tipjar: str
) -> Tip:
async def create_tip(tip: Tip) -> Tip:
"""Create a new Tip"""
await db.execute(
"""
INSERT INTO tipjar.Tips (
id,
wallet,
name,
message,
sats,
tipjar
)
VALUES (?, ?, ?, ?, ?, ?)
""",
(tip_id, wallet, name, message, sats, tipjar),
)

tip = await get_tip(tip_id)
assert tip, "Newly created tip couldn't be retrieved"
await db.insert("tipjar.tip", tip)
return tip


async def create_tipjar(data: CreateTipJar) -> TipJar:
"""Create a new TipJar"""

returning = "" if db.type == SQLITE else "RETURNING ID"
method = db.execute if db.type == SQLITE else db.fetchone

result = await method(
f"""
INSERT INTO tipjar.TipJars (
name,
wallet,
webhook,
onchain,
onchain_limit
)
VALUES (?, ?, ?, ?, ?)
{returning}
""",
(data.name, data.wallet, data.webhook, data.onchain, data.onchain_limit),
tipjar = TipJar(
id=urlsafe_short_hash(),
**data.dict(),
)
if db.type == SQLITE:
tipjar_id = result._result_proxy.lastrowid
else:
tipjar_id = result[0] # type: ignore

tipjar = await get_tipjar(tipjar_id)
assert tipjar
await db.insert("tipjar.tipjar", tipjar)
return tipjar


async def get_tipjar(tipjar_id: int) -> Optional[TipJar]:
async def get_tipjar(tipjar_id: str) -> Optional[TipJar]:
"""Return a tipjar by ID"""
row = await db.fetchone("SELECT * FROM tipjar.TipJars WHERE id = ?", (tipjar_id,))
return TipJar(**row) if row else None
return await db.fetchone(
"SELECT * FROM tipjar.tipjar WHERE id = :id",
{"id": tipjar_id},
TipJar,
)


async def get_tipjars(wallet_id: str) -> Optional[list]:
"""Return all TipJars belonging assigned to the wallet_id"""
rows = await db.fetchall(
"SELECT * FROM tipjar.TipJars WHERE wallet = ?", (wallet_id,)
return await db.fetchall(
"SELECT * FROM tipjar.tipjar WHERE wallet = :wallet_id",
{"wallet_id": wallet_id},
TipJar,
)
return [TipJar(**row) for row in rows] if rows else None


async def delete_tipjar(tipjar_id: int) -> None:
async def delete_tipjar(tipjar_id: str) -> None:
"""Delete a TipJar and all corresponding Tips"""
tips = await get_tipjar_tips(tipjar_id)
for tip in tips:
await delete_tip(tip.id)
await db.execute("DELETE FROM tipjar.TipJars WHERE id = ?", (tipjar_id,))
await db.execute("DELETE FROM tipjar.tipjar WHERE id = :id", {"id": tipjar_id})


async def get_tip(tip_id: str) -> Optional[Tip]:
"""Return a Tip"""
row = await db.fetchone("SELECT * FROM tipjar.Tips WHERE id = ?", (tip_id,))
return Tip(**row) if row else None
return await db.fetchone(
"SELECT * FROM tipjar.tip WHERE id = :id",
{"id": tip_id},
Tip,
)


async def get_tipjar_tips(tipjar_id: int) -> list[Tip]:
async def get_tipjar_tips(tipjar_id: str) -> list[Tip]:
"""Return all Tips for a tipjar"""
rows = await db.fetchall("SELECT * FROM tipjar.Tips WHERE tipjar = ?", (tipjar_id,))
return [Tip(**row) for row in rows]
return await db.fetchall(
"SELECT * FROM tipjar.tip WHERE tipjar = :tipjar_id",
{"tipjar_id": tipjar_id},
Tip,
)


async def get_tips(wallet_id: str) -> Optional[list]:
async def get_tips(wallet_id: str) -> list[Tip]:
"""Return all Tips assigned to wallet_id"""
rows = await db.fetchall("SELECT * FROM tipjar.Tips WHERE wallet = ?", (wallet_id,))
return [Tip(**row) for row in rows] if rows else None
return await db.fetchall(
"SELECT * FROM tipjar.tip WHERE wallet = :wallet_id",
{"wallet_id": wallet_id},
Tip,
)


async def delete_tip(tip_id: str) -> None:
"""Delete a Tip and its corresponding statspay charge"""
await db.execute("DELETE FROM tipjar.Tips WHERE id = ?", (tip_id,))
await db.execute("DELETE FROM tipjar.tip WHERE id = :id", {"id": tip_id})


async def update_tip(tip_id: str, **kwargs) -> Tip:
async def update_tip(tip: Tip) -> Tip:
"""Update a Tip"""
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
await db.execute(
f"UPDATE tipjar.Tips SET {q} WHERE id = ?", (*kwargs.values(), tip_id)
)
row = await db.fetchone("SELECT * FROM tipjar.Tips WHERE id = ?", (tip_id,))
assert row, "Newly updated tip couldn't be retrieved"
return Tip(**row)
await db.update("tipjar.tip", tip)
return tip


async def update_tipjar(tipjar_id: str, **kwargs) -> TipJar:
async def update_tipjar(tipjar: TipJar) -> TipJar:
"""Update a tipjar"""
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
await db.execute(
f"UPDATE tipjar.TipJars SET {q} WHERE id = ?", (*kwargs.values(), tipjar_id)
)
row = await db.fetchone("SELECT * FROM tipjar.TipJars WHERE id = ?", (tipjar_id,))
assert row, "Newly updated tipjar couldn't be retrieved"
return TipJar(**row)
await db.update("tipjar.tipjar", tipjar)
return tipjar
52 changes: 50 additions & 2 deletions migrations.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
async def m001_initial(db):
from lnbits.db import Database


async def m001_initial(db: Database):
await db.execute(
f"""
CREATE TABLE IF NOT EXISTS tipjar.TipJars (
Expand Down Expand Up @@ -26,9 +29,54 @@ async def m001_initial(db):
)


async def m002_add_onchain_limit(db):
async def m002_add_onchain_limit(db: Database):
await db.execute(
"""
ALTER TABLE tipjar.TipJars ADD COLUMN onchain_limit INTEGER;
"""
)


async def m003_tipjar_id_string_rename_tables(db: Database):
await db.execute(
"""
CREATE TABLE IF NOT EXISTS tipjar.tipjar (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
wallet TEXT NOT NULL,
onchain TEXT,
webhook TEXT,
onchain_limit INTEGER
);
"""
)
await db.execute(
"""
INSERT INTO tipjar.tipjar (id, name, wallet, onchain, webhook, onchain_limit)
SELECT id, name, wallet, onchain, webhook, onchain_limit
FROM tipjar.TipJars;
"""
)
await db.execute("DROP TABLE tipjar.TipJars;")
await db.execute(
f"""
CREATE TABLE IF NOT EXISTS tipjar.tip (
id TEXT PRIMARY KEY,
tipjar TEXT NOT NULL,
wallet TEXT NOT NULL,
name TEXT NOT NULL,
message TEXT NOT NULL,
sats {db.big_int} NOT NULL,
FOREIGN KEY(tipjar) REFERENCES {db.references_schema}tipjar(id)
);
"""
)
await db.execute(
"""
INSERT INTO tipjar.tip (id, tipjar, wallet, name, message, sats)
SELECT id, tipjar, wallet, name, message, sats
FROM tipjar.Tips;
"""
)
await db.execute("DROP TABLE tipjar.Tips;")
38 changes: 12 additions & 26 deletions models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from sqlite3 import Row
from typing import Optional

from pydantic import BaseModel
Expand All @@ -8,7 +7,7 @@ class CreateTip(BaseModel):
id: str
wallet: str
sats: int
tipjar: int
tipjar: str
name: str = "Anonymous"
message: str = ""

Expand All @@ -21,19 +20,7 @@ class Tip(BaseModel):
name: str # Name of the donor
message: str # Donation message
sats: int
tipjar: int # The ID of the corresponding tip jar

@classmethod
def from_row(cls, row: Row) -> "Tip":
return cls(**dict(row))


class CreateTipJar(BaseModel):
name: str
wallet: str
webhook: Optional[str]
onchain: Optional[str]
onchain_limit: Optional[int]
tipjar: str # The ID of the corresponding tip jar


class CreateTips(BaseModel):
Expand All @@ -43,16 +30,15 @@ class CreateTips(BaseModel):
message: str


class TipJar(BaseModel):
"""A TipJar represents a user's tip jar"""
class CreateTipJar(BaseModel):
name: str
wallet: str
webhook: Optional[str] = ""
onchain: Optional[str] = ""
onchain_limit: Optional[int] = 0

id: int
name: str # The name of the donatee
wallet: str # Lightning wallet
onchain: Optional[str] # Watchonly wallet
webhook: Optional[str] # URL to POST tips to
onchain_limit: Optional[int] # Bellow this amount, tips will be offchain only

@classmethod
def from_row(cls, row: Row) -> "TipJar":
return cls(**dict(row))
class TipJar(CreateTipJar):
"""A TipJar represents a user's tip jar"""

id: str
Loading

0 comments on commit d328213

Please sign in to comment.