-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmodels.py
More file actions
121 lines (95 loc) · 3.04 KB
/
models.py
File metadata and controls
121 lines (95 loc) · 3.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import base64
import hashlib
import json
from collections import OrderedDict
from fastapi import Request
from lnurl.types import LnurlPayMetadata
from pydantic import BaseModel
from .helpers import totp
shop_counters: dict = {}
class ShopCounter:
wordlist: list[str]
fulfilled_payments: OrderedDict
counter: int
@classmethod
def invoke(cls, shop: "Shop"):
shop_counter = shop_counters.get(shop.id)
if not shop_counter:
shop_counter = cls(wordlist=shop.wordlist.split("\n"))
shop_counters[shop.id] = shop_counter
return shop_counter
@classmethod
def reset(cls, shop: "Shop"):
shop_counter = cls.invoke(shop)
shop_counter.counter = -1
shop_counter.wordlist = shop.wordlist.split("\n")
def __init__(self, wordlist: list[str]):
self.wordlist = wordlist
self.fulfilled_payments = OrderedDict()
self.counter = -1
def get_word(self, payment_hash):
if payment_hash in self.fulfilled_payments:
return self.fulfilled_payments[payment_hash]
# get a new word
self.counter += 1
word = self.wordlist[self.counter % len(self.wordlist)]
self.fulfilled_payments[payment_hash] = word
# cleanup confirmation words cache
to_remove = len(self.fulfilled_payments) - 23
if to_remove > 0:
for _ in range(to_remove):
self.fulfilled_payments.popitem(False)
return word
class CreateShop(BaseModel):
wallet: str
method: str | None = "wordlist"
wordlist: str | None = None
class Shop(BaseModel):
id: str
wallet: str
method: str
wordlist: str
@property
def otp_key(self) -> str:
return base64.b32encode(
hashlib.sha256(
("otpkey" + str(self.id) + self.wallet).encode("ascii")
).digest()
).decode("ascii")
def get_code(self, payment_hash: str) -> str:
if self.method == "wordlist":
sc = ShopCounter.invoke(self)
return sc.get_word(payment_hash)
elif self.method == "totp":
return totp(self.otp_key)
return ""
class Item(BaseModel):
shop: str
id: str
name: str
description: str
image: str | None = None
enabled: bool | None = True
price: float
unit: str
def values(self, req: Request):
values = self.dict()
values["url"] = str(req.url_for("offlineshop.lnurl_response", item_id=self.id))
return values
@property
def lnurlpay_metadata(self) -> LnurlPayMetadata:
metadata = [("text/plain", self.description)]
if self.image:
try:
image_tuple = tuple(self.image.split(":")[1].split(",")[:2])
if len(image_tuple) == 2:
metadata.append(image_tuple)
except IndexError:
pass
return LnurlPayMetadata(json.dumps(metadata))
class CreateItem(BaseModel):
name: str
description: str
price: float
unit: str
image: str | None = None