mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2025-02-24 14:51:05 +01:00
Formatted gerty extension
This commit is contained in:
parent
1c56622d89
commit
07042a7d4f
17 changed files with 988 additions and 827 deletions
|
@ -34,6 +34,7 @@ from lnbits.utils.exchange_rates import (
|
|||
fiat_amount_as_satoshis,
|
||||
satoshis_amount_as_fiat,
|
||||
)
|
||||
|
||||
from .. import core_app, db
|
||||
from ..crud import (
|
||||
create_payment,
|
||||
|
|
|
@ -22,6 +22,8 @@ from lnbits.settings import (
|
|||
LNBITS_SITE_TITLE,
|
||||
SERVICE_FEE,
|
||||
)
|
||||
|
||||
from ...helpers import get_valid_extensions
|
||||
from ..crud import (
|
||||
create_account,
|
||||
create_wallet,
|
||||
|
@ -32,7 +34,6 @@ from ..crud import (
|
|||
update_user_extension,
|
||||
)
|
||||
from ..services import pay_invoice, redeem_lnurl_withdraw
|
||||
from ...helpers import get_valid_extensions
|
||||
|
||||
core_html_routes: APIRouter = APIRouter(tags=["Core NON-API Website Routes"])
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ from loguru import logger
|
|||
from starlette.requests import Request
|
||||
|
||||
from lnbits import bolt11
|
||||
|
||||
from .. import core_app
|
||||
from ..crud import get_standalone_payment
|
||||
from ..tasks import api_invoice_listeners
|
||||
|
|
|
@ -11,8 +11,10 @@ db = Database("ext_gerty")
|
|||
|
||||
gerty_ext: APIRouter = APIRouter(prefix="/gerty", tags=["Gerty"])
|
||||
|
||||
|
||||
def gerty_renderer():
|
||||
return template_renderer(["lnbits/extensions/gerty/templates"])
|
||||
|
||||
|
||||
from .views import * # noqa
|
||||
from .views_api import * # noqa
|
||||
|
|
|
@ -28,7 +28,7 @@ async def create_gerty(wallet_id: str, data: Gerty) -> Gerty:
|
|||
data.lnbits_wallets,
|
||||
data.mempool_endpoint,
|
||||
data.exchange,
|
||||
data.display_preferences
|
||||
data.display_preferences,
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -36,6 +36,7 @@ async def create_gerty(wallet_id: str, data: Gerty) -> Gerty:
|
|||
assert gerty, "Newly created gerty couldn't be retrieved"
|
||||
return gerty
|
||||
|
||||
|
||||
async def update_gerty(gerty_id: str, **kwargs) -> Gerty:
|
||||
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
||||
await db.execute(
|
||||
|
@ -43,6 +44,7 @@ async def update_gerty(gerty_id: str, **kwargs) -> Gerty:
|
|||
)
|
||||
return await get_gerty(gerty_id)
|
||||
|
||||
|
||||
async def get_gerty(gerty_id: str) -> Optional[Gerty]:
|
||||
row = await db.fetchone("SELECT * FROM gerty.gertys WHERE id = ?", (gerty_id,))
|
||||
return Gerty(**row) if row else None
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
from datetime import datetime, timedelta
|
||||
import pytz
|
||||
import httpx
|
||||
import textwrap
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import httpx
|
||||
import pytz
|
||||
from loguru import logger
|
||||
|
||||
from .number_prefixer import *
|
||||
|
||||
|
||||
def get_percent_difference(current, previous, precision=4):
|
||||
difference = (current - previous) / current * 100
|
||||
return "{0}{1}%".format("+" if difference > 0 else "", round(difference, precision))
|
||||
|
||||
|
||||
# A helper function get a nicely formated dict for the text
|
||||
def get_text_item_dict(text: str, font_size: int, x_pos: int = None, y_pos: int = None):
|
||||
# Get line size by font size
|
||||
|
@ -30,26 +33,24 @@ def get_text_item_dict(text: str, font_size: int, x_pos: int = None, y_pos: int
|
|||
word_list = wrapper.wrap(text=text)
|
||||
# logger.debug("number of chars = {0}".format(len(text)))
|
||||
|
||||
multilineText = '\n'.join(word_list)
|
||||
multilineText = "\n".join(word_list)
|
||||
# logger.debug("number of lines = {0}".format(len(word_list)))
|
||||
|
||||
# logger.debug('multilineText')
|
||||
# logger.debug(multilineText)
|
||||
|
||||
text = {
|
||||
"value": multilineText,
|
||||
"size": font_size
|
||||
}
|
||||
text = {"value": multilineText, "size": font_size}
|
||||
if x_pos is None and y_pos is None:
|
||||
text['position'] = 'center'
|
||||
text["position"] = "center"
|
||||
else:
|
||||
text['x'] = x_pos
|
||||
text['y'] = y_pos
|
||||
text["x"] = x_pos
|
||||
text["y"] = y_pos
|
||||
return text
|
||||
|
||||
|
||||
# format a number for nice display output
|
||||
def format_number(number, precision=None):
|
||||
return ("{:,}".format(round(number, precision)))
|
||||
return "{:,}".format(round(number, precision))
|
||||
|
||||
|
||||
async def get_mempool_recommended_fees(gerty):
|
||||
|
@ -58,6 +59,7 @@ async def get_mempool_recommended_fees(gerty):
|
|||
r = await client.get(gerty.mempool_endpoint + "/api/v1/fees/recommended")
|
||||
return r.json()
|
||||
|
||||
|
||||
async def get_mining_dashboard(gerty):
|
||||
areas = []
|
||||
if isinstance(gerty.mempool_endpoint, str):
|
||||
|
@ -65,94 +67,141 @@ async def get_mining_dashboard(gerty):
|
|||
# current hashrate
|
||||
r = await client.get(gerty.mempool_endpoint + "/api/v1/mining/hashrate/1w")
|
||||
data = r.json()
|
||||
hashrateNow = data['currentHashrate']
|
||||
hashrateOneWeekAgo = data['hashrates'][6]['avgHashrate']
|
||||
hashrateNow = data["currentHashrate"]
|
||||
hashrateOneWeekAgo = data["hashrates"][6]["avgHashrate"]
|
||||
|
||||
text = []
|
||||
text.append(get_text_item_dict("Current mining hashrate", 12))
|
||||
text.append(get_text_item_dict("{0}hash".format(si_format(hashrateNow, 6, True, " ")), 20))
|
||||
text.append(get_text_item_dict("{0} vs 7 days ago".format(get_percent_difference(hashrateNow, hashrateOneWeekAgo, 3)), 12))
|
||||
text.append(
|
||||
get_text_item_dict(
|
||||
"{0}hash".format(si_format(hashrateNow, 6, True, " ")), 20
|
||||
)
|
||||
)
|
||||
text.append(
|
||||
get_text_item_dict(
|
||||
"{0} vs 7 days ago".format(
|
||||
get_percent_difference(hashrateNow, hashrateOneWeekAgo, 3)
|
||||
),
|
||||
12,
|
||||
)
|
||||
)
|
||||
areas.append(text)
|
||||
|
||||
r = await client.get(gerty.mempool_endpoint + "/api/v1/difficulty-adjustment")
|
||||
r = await client.get(
|
||||
gerty.mempool_endpoint + "/api/v1/difficulty-adjustment"
|
||||
)
|
||||
|
||||
# timeAvg
|
||||
text = []
|
||||
progress = "{0}%".format(round(r.json()['progressPercent'], 2))
|
||||
progress = "{0}%".format(round(r.json()["progressPercent"], 2))
|
||||
text.append(get_text_item_dict("Progress through current epoch", 12))
|
||||
text.append(get_text_item_dict(progress, 20))
|
||||
areas.append(text)
|
||||
|
||||
# difficulty adjustment
|
||||
text = []
|
||||
stat = r.json()['remainingTime']
|
||||
stat = r.json()["remainingTime"]
|
||||
text.append(get_text_item_dict("Time to next difficulty adjustment", 12))
|
||||
text.append(get_text_item_dict(get_time_remaining(stat / 1000, 3), 20))
|
||||
areas.append(text)
|
||||
|
||||
# difficultyChange
|
||||
text = []
|
||||
difficultyChange = round(r.json()['difficultyChange'], 2)
|
||||
difficultyChange = round(r.json()["difficultyChange"], 2)
|
||||
text.append(get_text_item_dict("Estimated difficulty change", 12))
|
||||
text.append(get_text_item_dict("{0}{1}%".format("+" if difficultyChange > 0 else "", round(difficultyChange, 2)), 20))
|
||||
text.append(
|
||||
get_text_item_dict(
|
||||
"{0}{1}%".format(
|
||||
"+" if difficultyChange > 0 else "", round(difficultyChange, 2)
|
||||
),
|
||||
20,
|
||||
)
|
||||
)
|
||||
areas.append(text)
|
||||
|
||||
r = await client.get(gerty.mempool_endpoint + "/api/v1/mining/hashrate/1m")
|
||||
data = r.json()
|
||||
stat = {}
|
||||
stat['current'] = data['currentDifficulty']
|
||||
stat['previous'] = data['difficulty'][len(data['difficulty']) - 2]['difficulty']
|
||||
stat["current"] = data["currentDifficulty"]
|
||||
stat["previous"] = data["difficulty"][len(data["difficulty"]) - 2][
|
||||
"difficulty"
|
||||
]
|
||||
return areas
|
||||
|
||||
|
||||
async def api_get_lightning_stats(gerty):
|
||||
stat = {}
|
||||
if isinstance(gerty.mempool_endpoint, str):
|
||||
async with httpx.AsyncClient() as client:
|
||||
r = await client.get(gerty.mempool_endpoint + "/api/v1/lightning/statistics/latest")
|
||||
r = await client.get(
|
||||
gerty.mempool_endpoint + "/api/v1/lightning/statistics/latest"
|
||||
)
|
||||
data = r.json()
|
||||
return data
|
||||
|
||||
|
||||
async def get_lightning_stats(gerty):
|
||||
data = await api_get_lightning_stats(gerty)
|
||||
areas = []
|
||||
|
||||
text = []
|
||||
text.append(get_text_item_dict("Channel Count", 12))
|
||||
text.append(get_text_item_dict(format_number(data['latest']['channel_count']), 20))
|
||||
difference = get_percent_difference(current=data['latest']['channel_count'],
|
||||
previous=data['previous']['channel_count'])
|
||||
text.append(get_text_item_dict(format_number(data["latest"]["channel_count"]), 20))
|
||||
difference = get_percent_difference(
|
||||
current=data["latest"]["channel_count"],
|
||||
previous=data["previous"]["channel_count"],
|
||||
)
|
||||
text.append(get_text_item_dict("{0} in last 7 days".format(difference), 12))
|
||||
areas.append(text)
|
||||
|
||||
text = []
|
||||
text.append(get_text_item_dict("Number of Nodes", 12))
|
||||
text.append(get_text_item_dict(format_number(data['latest']['node_count']), 20))
|
||||
difference = get_percent_difference(current=data['latest']['node_count'], previous=data['previous']['node_count'])
|
||||
text.append(get_text_item_dict(format_number(data["latest"]["node_count"]), 20))
|
||||
difference = get_percent_difference(
|
||||
current=data["latest"]["node_count"], previous=data["previous"]["node_count"]
|
||||
)
|
||||
text.append(get_text_item_dict("{0} in last 7 days".format(difference), 12))
|
||||
areas.append(text)
|
||||
|
||||
text = []
|
||||
text.append(get_text_item_dict("Total Capacity", 12))
|
||||
avg_capacity = float(data['latest']['total_capacity']) / float(100000000)
|
||||
text.append(get_text_item_dict("{0} BTC".format(format_number(avg_capacity, 2)), 20))
|
||||
difference = get_percent_difference(current=data['latest']['total_capacity'], previous=data['previous']['total_capacity'])
|
||||
avg_capacity = float(data["latest"]["total_capacity"]) / float(100000000)
|
||||
text.append(
|
||||
get_text_item_dict("{0} BTC".format(format_number(avg_capacity, 2)), 20)
|
||||
)
|
||||
difference = get_percent_difference(
|
||||
current=data["latest"]["total_capacity"],
|
||||
previous=data["previous"]["total_capacity"],
|
||||
)
|
||||
text.append(get_text_item_dict("{0} in last 7 days".format(difference), 12))
|
||||
areas.append(text)
|
||||
|
||||
text = []
|
||||
text.append(get_text_item_dict("Average Channel Capacity", 12))
|
||||
text.append(get_text_item_dict("{0} sats".format(format_number(data['latest']['avg_capacity'])), 20))
|
||||
difference = get_percent_difference(current=data['latest']['avg_capacity'], previous=data['previous']['avg_capacity'])
|
||||
text.append(
|
||||
get_text_item_dict(
|
||||
"{0} sats".format(format_number(data["latest"]["avg_capacity"])), 20
|
||||
)
|
||||
)
|
||||
difference = get_percent_difference(
|
||||
current=data["latest"]["avg_capacity"],
|
||||
previous=data["previous"]["avg_capacity"],
|
||||
)
|
||||
text.append(get_text_item_dict("{0} in last 7 days".format(difference), 12))
|
||||
areas.append(text)
|
||||
|
||||
return areas
|
||||
|
||||
|
||||
def get_next_update_time(sleep_time_seconds: int = 0, timezone: str = "Europe/London"):
|
||||
utc_now = pytz.utc.localize(datetime.utcnow())
|
||||
next_refresh_time = utc_now + timedelta(0, sleep_time_seconds)
|
||||
local_refresh_time = next_refresh_time.astimezone(pytz.timezone(timezone))
|
||||
return "{0} {1}".format("I'll wake up at" if gerty_should_sleep() else "Next update at",local_refresh_time.strftime("%H:%M on %e %b %Y"))
|
||||
return "{0} {1}".format(
|
||||
"I'll wake up at" if gerty_should_sleep() else "Next update at",
|
||||
local_refresh_time.strftime("%H:%M on %e %b %Y"),
|
||||
)
|
||||
|
||||
|
||||
def gerty_should_sleep(timezone: str = "Europe/London"):
|
||||
utc_now = pytz.utc.localize(datetime.utcnow())
|
||||
|
@ -161,13 +210,12 @@ def gerty_should_sleep(timezone: str = "Europe/London"):
|
|||
hours = int(hours)
|
||||
logger.debug("HOURS")
|
||||
logger.debug(hours)
|
||||
if(hours >= 22 and hours <= 23):
|
||||
if hours >= 22 and hours <= 23:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def get_date_suffix(dayNumber):
|
||||
if 4 <= dayNumber <= 20 or 24 <= dayNumber <= 30:
|
||||
return "th"
|
||||
|
@ -178,10 +226,10 @@ def get_date_suffix(dayNumber):
|
|||
def get_time_remaining(seconds, granularity=2):
|
||||
intervals = (
|
||||
# ('weeks', 604800), # 60 * 60 * 24 * 7
|
||||
('days', 86400), # 60 * 60 * 24
|
||||
('hours', 3600), # 60 * 60
|
||||
('minutes', 60),
|
||||
('seconds', 1),
|
||||
("days", 86400), # 60 * 60 * 24
|
||||
("hours", 3600), # 60 * 60
|
||||
("minutes", 60),
|
||||
("seconds", 1),
|
||||
)
|
||||
|
||||
result = []
|
||||
|
@ -191,6 +239,6 @@ def get_time_remaining(seconds, granularity=2):
|
|||
if value:
|
||||
seconds -= value * count
|
||||
if value == 1:
|
||||
name = name.rstrip('s')
|
||||
name = name.rstrip("s")
|
||||
result.append("{} {}".format(round(value), name))
|
||||
return ', '.join(result[:granularity])
|
||||
return ", ".join(result[:granularity])
|
||||
|
|
|
@ -4,14 +4,19 @@ from typing import Optional
|
|||
from fastapi import Query
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Gerty(BaseModel):
|
||||
id: str = Query(None)
|
||||
name: str
|
||||
wallet: str
|
||||
refresh_time: int = Query(None)
|
||||
lnbits_wallets: str = Query(None) # Wallets to keep an eye on, {"wallet-id": "wallet-read-key, etc"}
|
||||
lnbits_wallets: str = Query(
|
||||
None
|
||||
) # Wallets to keep an eye on, {"wallet-id": "wallet-read-key, etc"}
|
||||
mempool_endpoint: str = Query(None) # Mempool endpoint to use
|
||||
exchange: str = Query(None) # BTC <-> Fiat exchange rate to pull ie "USD", in 0.0001 and sats
|
||||
exchange: str = Query(
|
||||
None
|
||||
) # BTC <-> Fiat exchange rate to pull ie "USD", in 0.0001 and sats
|
||||
display_preferences: str = Query(None)
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -1,48 +1,51 @@
|
|||
import math
|
||||
|
||||
|
||||
def si_classifier(val):
|
||||
suffixes = {
|
||||
24:{'long_suffix':'yotta', 'short_suffix':'Y', 'scalar':10**24},
|
||||
21:{'long_suffix':'zetta', 'short_suffix':'Z', 'scalar':10**21},
|
||||
18:{'long_suffix':'exa', 'short_suffix':'E', 'scalar':10**18},
|
||||
15:{'long_suffix':'peta', 'short_suffix':'P', 'scalar':10**15},
|
||||
12:{'long_suffix':'tera', 'short_suffix':'T', 'scalar':10**12},
|
||||
9:{'long_suffix':'giga', 'short_suffix':'G', 'scalar':10**9},
|
||||
6:{'long_suffix':'mega', 'short_suffix':'M', 'scalar':10**6},
|
||||
3:{'long_suffix':'kilo', 'short_suffix':'k', 'scalar':10**3},
|
||||
0:{'long_suffix':'', 'short_suffix':'', 'scalar':10**0},
|
||||
-3:{'long_suffix':'milli', 'short_suffix':'m', 'scalar':10**-3},
|
||||
-6:{'long_suffix':'micro', 'short_suffix':'µ', 'scalar':10**-6},
|
||||
-9:{'long_suffix':'nano', 'short_suffix':'n', 'scalar':10**-9},
|
||||
-12:{'long_suffix':'pico', 'short_suffix':'p', 'scalar':10**-12},
|
||||
-15:{'long_suffix':'femto', 'short_suffix':'f', 'scalar':10**-15},
|
||||
-18:{'long_suffix':'atto', 'short_suffix':'a', 'scalar':10**-18},
|
||||
-21:{'long_suffix':'zepto', 'short_suffix':'z', 'scalar':10**-21},
|
||||
-24:{'long_suffix':'yocto', 'short_suffix':'y', 'scalar':10**-24}
|
||||
24: {"long_suffix": "yotta", "short_suffix": "Y", "scalar": 10**24},
|
||||
21: {"long_suffix": "zetta", "short_suffix": "Z", "scalar": 10**21},
|
||||
18: {"long_suffix": "exa", "short_suffix": "E", "scalar": 10**18},
|
||||
15: {"long_suffix": "peta", "short_suffix": "P", "scalar": 10**15},
|
||||
12: {"long_suffix": "tera", "short_suffix": "T", "scalar": 10**12},
|
||||
9: {"long_suffix": "giga", "short_suffix": "G", "scalar": 10**9},
|
||||
6: {"long_suffix": "mega", "short_suffix": "M", "scalar": 10**6},
|
||||
3: {"long_suffix": "kilo", "short_suffix": "k", "scalar": 10**3},
|
||||
0: {"long_suffix": "", "short_suffix": "", "scalar": 10**0},
|
||||
-3: {"long_suffix": "milli", "short_suffix": "m", "scalar": 10**-3},
|
||||
-6: {"long_suffix": "micro", "short_suffix": "µ", "scalar": 10**-6},
|
||||
-9: {"long_suffix": "nano", "short_suffix": "n", "scalar": 10**-9},
|
||||
-12: {"long_suffix": "pico", "short_suffix": "p", "scalar": 10**-12},
|
||||
-15: {"long_suffix": "femto", "short_suffix": "f", "scalar": 10**-15},
|
||||
-18: {"long_suffix": "atto", "short_suffix": "a", "scalar": 10**-18},
|
||||
-21: {"long_suffix": "zepto", "short_suffix": "z", "scalar": 10**-21},
|
||||
-24: {"long_suffix": "yocto", "short_suffix": "y", "scalar": 10**-24},
|
||||
}
|
||||
exponent = int(math.floor(math.log10(abs(val)) / 3.0) * 3)
|
||||
return suffixes.get(exponent, None)
|
||||
|
||||
|
||||
def si_formatter(value):
|
||||
'''
|
||||
"""
|
||||
Return a triple of scaled value, short suffix, long suffix, or None if
|
||||
the value cannot be classified.
|
||||
'''
|
||||
"""
|
||||
classifier = si_classifier(value)
|
||||
if classifier == None:
|
||||
# Don't know how to classify this value
|
||||
return None
|
||||
|
||||
scaled = value / classifier['scalar']
|
||||
return (scaled, classifier['short_suffix'], classifier['long_suffix'])
|
||||
scaled = value / classifier["scalar"]
|
||||
return (scaled, classifier["short_suffix"], classifier["long_suffix"])
|
||||
|
||||
def si_format(value, precision=4, long_form=False, separator=''):
|
||||
'''
|
||||
|
||||
def si_format(value, precision=4, long_form=False, separator=""):
|
||||
"""
|
||||
"SI prefix" formatted string: return a string with the given precision
|
||||
and an appropriate order-of-3-magnitudes suffix, e.g.:
|
||||
si_format(1001.0) => '1.00K'
|
||||
si_format(0.00000000123, long_form=True, separator=' ') => '1.230 nano'
|
||||
'''
|
||||
"""
|
||||
scaled, short_suffix, long_suffix = si_formatter(value)
|
||||
|
||||
if scaled == None:
|
||||
|
@ -58,5 +61,6 @@ def si_format(value, precision=4, long_form=False, separator=''):
|
|||
else:
|
||||
precision = precision - 3
|
||||
|
||||
return '{scaled:.{precision}f}{separator}{suffix}'.format(
|
||||
scaled=scaled, precision=precision, separator=separator, suffix=suffix)
|
||||
return "{scaled:.{precision}f}{separator}{suffix}".format(
|
||||
scaled=scaled, precision=precision, separator=separator, suffix=suffix
|
||||
)
|
||||
|
|
|
@ -71,7 +71,8 @@
|
|||
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
|
||||
<code
|
||||
>curl -X DELETE {{ request.base_url
|
||||
}}gerty/api/v1/gertys/<gerty_id> -H "X-Api-Key: <admin_key>"
|
||||
}}gerty/api/v1/gertys/<gerty_id> -H "X-Api-Key:
|
||||
<admin_key>"
|
||||
</code>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
|
|
@ -1,25 +1,50 @@
|
|||
{% extends "public.html" %} {% block toolbar_title %} {{ gerty.name }}{% endblock %}{% block page %}
|
||||
{% raw %}
|
||||
{% extends "public.html" %} {% block toolbar_title %} {{ gerty.name }}{%
|
||||
endblock %}{% block page %} {% raw %}
|
||||
<div class="q-pa-md row items-start q-gutter-md">
|
||||
<q-card unelevated flat class="q-pa-none text-body1 blockquote" style="background: none !important;">
|
||||
<q-card
|
||||
unelevated
|
||||
flat
|
||||
class="q-pa-none text-body1 blockquote"
|
||||
style="background: none !important"
|
||||
>
|
||||
"{{gerty.sats_quote[0].text}}" <br />~ Satoshi {{gerty.sats_quote[0].date}}
|
||||
</q-card>
|
||||
</div>
|
||||
<div class="q-pa-md row items-start q-gutter-md">
|
||||
<q-card unelevated flat class="q-pa-none" style="background: none !important;">
|
||||
<q-card unelevated flat class="q-pa-none" style="background: none !important">
|
||||
<q-card-section class="text-h1 q-pa-none">
|
||||
{{gerty.exchange[0].amount.toFixed(2)}} {{gerty.exchange[0].fiat}}
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
<q-card v-for="gertywallet in gertywallets" style="width: 380px" flat>
|
||||
<q-card-section horizontal class="q-pa-none" :style="`background-color: ${gertywallet.color1}`">
|
||||
<q-card-section
|
||||
horizontal
|
||||
class="q-pa-none"
|
||||
:style="`background-color: ${gertywallet.color1}`"
|
||||
>
|
||||
<q-card-section class="q-pa-none">
|
||||
<div class="q-item__section column q-pa-lg q-mr-none text-white q-item__section--side justify-center" :style="`background-color: ${gertywallet.color2}`" >
|
||||
<i aria-hidden="true" role="presentation" class="material-icons q-icon notranslate text-white" style="font-size: 50px;">sentiment_satisfied</i></div>
|
||||
<div
|
||||
class="q-item__section column q-pa-lg q-mr-none text-white q-item__section--side justify-center"
|
||||
:style="`background-color: ${gertywallet.color2}`"
|
||||
>
|
||||
<i
|
||||
aria-hidden="true"
|
||||
role="presentation"
|
||||
class="material-icons q-icon notranslate text-white"
|
||||
style="font-size: 50px"
|
||||
>sentiment_satisfied</i
|
||||
>
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
||||
<div class="q-item__section column q-pa-md q-ml-none text-white q-item__section--main justify-center" style="min-width:200px;">
|
||||
<div class="q-item__label text-white text-h6 text-weight-bolder">{{gertywallet.amount}}</div><div class="q-item__label"><b>{{gertywallet.name}}</b></div>
|
||||
<div
|
||||
class="q-item__section column q-pa-md q-ml-none text-white q-item__section--main justify-center"
|
||||
style="min-width: 200px"
|
||||
>
|
||||
<div class="q-item__label text-white text-h6 text-weight-bolder">
|
||||
{{gertywallet.amount}}
|
||||
</div>
|
||||
<div class="q-item__label"><b>{{gertywallet.name}}</b></div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
@ -30,9 +55,18 @@
|
|||
<q-card class="q-pa-lg">
|
||||
<p class="text-h4">Onchain Stats</p>
|
||||
Difficulty Progress Percent
|
||||
<q-linear-progress size="20px" :value="gerty.onchain[0].difficulty[0].progressPercent/100" color="primary" class="q-mt-sm">
|
||||
<q-linear-progress
|
||||
size="20px"
|
||||
:value="gerty.onchain[0].difficulty[0].progressPercent/100"
|
||||
color="primary"
|
||||
class="q-mt-sm"
|
||||
>
|
||||
<div class="absolute-full flex flex-center">
|
||||
<q-badge color="white" text-color="accent" :label="gerty.onchain[0].difficulty[0].progressPercent.toFixed() + '%'" />
|
||||
<q-badge
|
||||
color="white"
|
||||
text-color="accent"
|
||||
:label="gerty.onchain[0].difficulty[0].progressPercent.toFixed() + '%'"
|
||||
/>
|
||||
</div>
|
||||
</q-linear-progress>
|
||||
|
||||
|
@ -48,15 +82,12 @@
|
|||
<q-card class="q-pa-lg">
|
||||
<p class="text-h4">LN Stats</p>
|
||||
<q-card-section class="q-pa-none">
|
||||
<div class="row q-mt-lg q-gutter-sm">
|
||||
{{gerty.ln}}
|
||||
</div>
|
||||
<div class="row q-mt-lg q-gutter-sm">{{gerty.ln}}</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
</div>
|
||||
{% endraw %}
|
||||
{% endblock %} {% block scripts %}
|
||||
{% endraw %} {% endblock %} {% block scripts %}
|
||||
<script>
|
||||
Vue.component(VueQrcode.name, VueQrcode)
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context %} {% block page %}
|
||||
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
|
||||
%} {% block page %}
|
||||
<div class="row q-col-gutter-md">
|
||||
<div class="col-12 col-md-8 col-lg-7 q-gutter-y-md">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<q-btn unelevated color="primary" @click="formDialog.show = true"
|
||||
>New Gerty
|
||||
</q-btn
|
||||
>
|
||||
</q-btn>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
<q-card>
|
||||
|
@ -31,8 +31,12 @@
|
|||
<template v-slot:header="props">
|
||||
<q-tr :props="props">
|
||||
<q-th auto-width></q-th>
|
||||
<q-th v-for="col in props.cols" :key="col.name" :props="props"
|
||||
:class="`col__${col.name} text-truncate elipsis`">
|
||||
<q-th
|
||||
v-for="col in props.cols"
|
||||
:key="col.name"
|
||||
:props="props"
|
||||
:class="`col__${col.name} text-truncate elipsis`"
|
||||
>
|
||||
{{ col.label }}
|
||||
</q-th>
|
||||
<q-th auto-width></q-th>
|
||||
|
@ -103,13 +107,13 @@
|
|||
<div class="col-12 col-md-5 q-gutter-y-md">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<h6 class="text-subtitle1 q-my-none">{{ SITE_TITLE }} Gerty extension</h6>
|
||||
<h6 class="text-subtitle1 q-my-none">
|
||||
{{ SITE_TITLE }} Gerty extension
|
||||
</h6>
|
||||
</q-card-section>
|
||||
<q-card-section class="q-pa-none">
|
||||
<q-separator></q-separator>
|
||||
<q-list>
|
||||
{% include "gerty/_api_docs.html" %}
|
||||
</q-list>
|
||||
<q-list> {% include "gerty/_api_docs.html" %} </q-list>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
|
@ -170,7 +174,9 @@
|
|||
v-model.trim="formDialog.data.refresh_time"
|
||||
label="Refresh time in seconds"
|
||||
>
|
||||
<q-tooltip>The amount of time in seconds between screen updates</q-tooltip>
|
||||
<q-tooltip
|
||||
>The amount of time in seconds between screen updates</q-tooltip
|
||||
>
|
||||
</q-input>
|
||||
|
||||
<p>Use the toggles below to control what your Gerty will display</p>
|
||||
|
@ -220,8 +226,7 @@
|
|||
class="q-mr-md"
|
||||
v-if="!formDialog.data.id"
|
||||
>Create Gerty
|
||||
</q-btn
|
||||
>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
v-else
|
||||
unelevated
|
||||
|
@ -229,12 +234,10 @@
|
|||
:disable="formDialog.data.wallet == null || formDialog.data.name == null"
|
||||
type="submit"
|
||||
>Update Gerty
|
||||
</q-btn
|
||||
>
|
||||
</q-btn>
|
||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
|
||||
>Cancel
|
||||
</q-btn
|
||||
>
|
||||
</q-btn>
|
||||
</div>
|
||||
</q-form>
|
||||
</q-card>
|
||||
|
@ -619,12 +622,10 @@
|
|||
}
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
{% endblock %} {% block styles %}
|
||||
<style>
|
||||
.col__display_preferences {
|
||||
border: 1px solid red
|
||||
border: 1px solid red;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
|
@ -1,8 +1,10 @@
|
|||
import json
|
||||
from http import HTTPStatus
|
||||
|
||||
from fastapi import Request
|
||||
from fastapi.params import Depends
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from loguru import logger
|
||||
from starlette.exceptions import HTTPException
|
||||
from starlette.responses import HTMLResponse
|
||||
|
||||
|
@ -14,18 +16,16 @@ from . import gerty_ext, gerty_renderer
|
|||
from .crud import get_gerty
|
||||
from .views_api import api_gerty_json
|
||||
|
||||
import json
|
||||
|
||||
from loguru import logger
|
||||
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
|
||||
@gerty_ext.get("/", response_class=HTMLResponse)
|
||||
async def index(request: Request, user: User = Depends(check_user_exists)):
|
||||
return gerty_renderer().TemplateResponse(
|
||||
"gerty/index.html", {"request": request, "user": user.dict()}
|
||||
)
|
||||
|
||||
|
||||
@gerty_ext.get("/{gerty_id}", response_class=HTMLResponse)
|
||||
async def display(request: Request, gerty_id):
|
||||
gerty = await get_gerty(gerty_id)
|
||||
|
@ -34,4 +34,6 @@ async def display(request: Request, gerty_id):
|
|||
status_code=HTTPStatus.NOT_FOUND, detail="Gerty does not exist."
|
||||
)
|
||||
gertyData = await api_gerty_json(gerty_id)
|
||||
return gerty_renderer().TemplateResponse("gerty/gerty.html", {"request": request, "gerty": gertyData})
|
||||
return gerty_renderer().TemplateResponse(
|
||||
"gerty/gerty.html", {"request": request, "gerty": gertyData}
|
||||
)
|
||||
|
|
|
@ -1,32 +1,30 @@
|
|||
import math
|
||||
from http import HTTPStatus
|
||||
import json
|
||||
import httpx
|
||||
import random
|
||||
import math
|
||||
import os
|
||||
import random
|
||||
import time
|
||||
from datetime import datetime
|
||||
from http import HTTPStatus
|
||||
|
||||
import httpx
|
||||
from fastapi import Query
|
||||
from fastapi.params import Depends
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from lnurl import decode as decode_lnurl
|
||||
from loguru import logger
|
||||
from starlette.exceptions import HTTPException
|
||||
|
||||
from lnbits.core.crud import get_wallet_for_key
|
||||
from lnbits.core.crud import get_user
|
||||
from lnbits.core.crud import get_user, get_wallet_for_key
|
||||
from lnbits.core.services import create_invoice
|
||||
from lnbits.core.views.api import api_payment, api_wallet
|
||||
from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
from .helpers import *
|
||||
|
||||
from . import gerty_ext
|
||||
from .crud import create_gerty, update_gerty, delete_gerty, get_gerty, get_gertys
|
||||
from .models import Gerty
|
||||
|
||||
from lnbits.utils.exchange_rates import satoshis_amount_as_fiat
|
||||
|
||||
from ...settings import LNBITS_PATH
|
||||
from . import gerty_ext
|
||||
from .crud import create_gerty, delete_gerty, get_gerty, get_gertys, update_gerty
|
||||
from .helpers import *
|
||||
from .models import Gerty
|
||||
|
||||
|
||||
@gerty_ext.get("/api/v1/gerty", status_code=HTTPStatus.OK)
|
||||
|
@ -88,10 +86,11 @@ async def api_gerty_delete(
|
|||
|
||||
#######################
|
||||
|
||||
|
||||
@gerty_ext.get("/api/v1/gerty/satoshiquote", status_code=HTTPStatus.OK)
|
||||
async def api_gerty_satoshi():
|
||||
maxQuoteLength = 186;
|
||||
with open(os.path.join(LNBITS_PATH, 'extensions/gerty/static/satoshi.json')) as fd:
|
||||
maxQuoteLength = 186
|
||||
with open(os.path.join(LNBITS_PATH, "extensions/gerty/static/satoshi.json")) as fd:
|
||||
satoshiQuotes = json.load(fd)
|
||||
quote = satoshiQuotes[random.randint(0, len(satoshiQuotes) - 1)]
|
||||
# logger.debug(quote.text)
|
||||
|
@ -103,10 +102,7 @@ async def api_gerty_satoshi():
|
|||
|
||||
|
||||
@gerty_ext.get("/api/v1/gerty/{gerty_id}/{p}")
|
||||
async def api_gerty_json(
|
||||
gerty_id: str,
|
||||
p: int = None # page number
|
||||
):
|
||||
async def api_gerty_json(gerty_id: str, p: int = None): # page number
|
||||
gerty = await get_gerty(gerty_id)
|
||||
|
||||
if not gerty:
|
||||
|
@ -129,7 +125,7 @@ async def api_gerty_json(
|
|||
logger.debug("Screeens " + str(enabled_screens))
|
||||
data = await get_screen_data(p, enabled_screens, gerty)
|
||||
|
||||
next_screen_number = 0 if ((p + 1) >= enabled_screen_count) else p + 1;
|
||||
next_screen_number = 0 if ((p + 1) >= enabled_screen_count) else p + 1
|
||||
|
||||
# get the sleep time
|
||||
sleep_time = gerty.refresh_time if gerty.refresh_time else 300
|
||||
|
@ -143,14 +139,14 @@ async def api_gerty_json(
|
|||
"requestTimestamp": get_next_update_time(sleep_time),
|
||||
"nextScreenNumber": next_screen_number,
|
||||
"showTextBoundRect": False,
|
||||
"name": gerty.name
|
||||
"name": gerty.name,
|
||||
},
|
||||
"screen": {
|
||||
"slug": get_screen_slug_by_index(p, enabled_screens),
|
||||
"group": get_screen_slug_by_index(p, enabled_screens),
|
||||
"title": data['title'],
|
||||
"areas": data['areas']
|
||||
}
|
||||
"title": data["title"],
|
||||
"areas": data["areas"],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
@ -163,7 +159,7 @@ def get_screen_slug_by_index(index: int, screens_list):
|
|||
async def get_screen_data(screen_num: int, screens_list: dict, gerty):
|
||||
screen_slug = get_screen_slug_by_index(screen_num, screens_list)
|
||||
# first get the relevant slug from the display_preferences
|
||||
logger.debug('screen_slug')
|
||||
logger.debug("screen_slug")
|
||||
logger.debug(screen_slug)
|
||||
areas = []
|
||||
title = ""
|
||||
|
@ -188,8 +184,8 @@ async def get_screen_data(screen_num: int, screens_list: dict, gerty):
|
|||
areas = await get_lightning_stats(gerty)
|
||||
|
||||
data = {}
|
||||
data['title'] = title
|
||||
data['areas'] = areas
|
||||
data["title"] = title
|
||||
data["areas"] = areas
|
||||
|
||||
return data
|
||||
|
||||
|
@ -208,8 +204,10 @@ async def get_dashboard(gerty):
|
|||
wallets = await get_lnbits_wallet_balances(gerty)
|
||||
text = []
|
||||
for wallet in wallets:
|
||||
text.append(get_text_item_dict("{0}".format(wallet['name']), 15))
|
||||
text.append(get_text_item_dict("{0} sats".format(format_number(wallet['balance'])), 20))
|
||||
text.append(get_text_item_dict("{0}".format(wallet["name"]), 15))
|
||||
text.append(
|
||||
get_text_item_dict("{0} sats".format(format_number(wallet["balance"])), 20)
|
||||
)
|
||||
areas.append(text)
|
||||
|
||||
# Mempool fees
|
||||
|
@ -220,7 +218,11 @@ async def get_dashboard(gerty):
|
|||
|
||||
# difficulty adjustment time
|
||||
text = []
|
||||
text.append(get_text_item_dict(await get_time_remaining_next_difficulty_adjustment(gerty), 15))
|
||||
text.append(
|
||||
get_text_item_dict(
|
||||
await get_time_remaining_next_difficulty_adjustment(gerty), 15
|
||||
)
|
||||
)
|
||||
text.append(get_text_item_dict("until next difficulty adjustment", 12))
|
||||
areas.append(text)
|
||||
|
||||
|
@ -235,18 +237,20 @@ async def get_lnbits_wallet_balances(gerty):
|
|||
wallet = await get_wallet_for_key(key=lnbits_wallet)
|
||||
logger.debug(wallet.name)
|
||||
if wallet:
|
||||
wallets.append({
|
||||
wallets.append(
|
||||
{
|
||||
"name": wallet.name,
|
||||
"balance": wallet.balance_msat / 1000,
|
||||
"inkey": wallet.inkey,
|
||||
})
|
||||
}
|
||||
)
|
||||
return wallets
|
||||
|
||||
|
||||
async def get_placeholder_text():
|
||||
return [
|
||||
get_text_item_dict("Some placeholder text", 15, 10, 50),
|
||||
get_text_item_dict("Some placeholder text", 15, 10, 50)
|
||||
get_text_item_dict("Some placeholder text", 15, 10, 50),
|
||||
]
|
||||
|
||||
|
||||
|
@ -255,10 +259,12 @@ async def get_satoshi_quotes():
|
|||
text = []
|
||||
quote = await api_gerty_satoshi()
|
||||
if quote:
|
||||
if quote['text']:
|
||||
text.append(get_text_item_dict(quote['text'], 15))
|
||||
if quote['date']:
|
||||
text.append(get_text_item_dict("Satoshi Nakamoto - {0}".format(quote['date']), 15))
|
||||
if quote["text"]:
|
||||
text.append(get_text_item_dict(quote["text"], 15))
|
||||
if quote["date"]:
|
||||
text.append(
|
||||
get_text_item_dict("Satoshi Nakamoto - {0}".format(quote["date"]), 15)
|
||||
)
|
||||
return text
|
||||
|
||||
|
||||
|
@ -270,7 +276,11 @@ async def get_exchange_rate(gerty):
|
|||
amount = await satoshis_amount_as_fiat(100000000, gerty.exchange)
|
||||
if amount:
|
||||
price = format_number(amount)
|
||||
text.append(get_text_item_dict("Current {0}/BTC price".format(gerty.exchange), 15))
|
||||
text.append(
|
||||
get_text_item_dict(
|
||||
"Current {0}/BTC price".format(gerty.exchange), 15
|
||||
)
|
||||
)
|
||||
text.append(get_text_item_dict(price, 80))
|
||||
except:
|
||||
pass
|
||||
|
@ -281,29 +291,43 @@ async def get_onchain_dashboard(gerty):
|
|||
areas = []
|
||||
if isinstance(gerty.mempool_endpoint, str):
|
||||
async with httpx.AsyncClient() as client:
|
||||
r = await client.get(gerty.mempool_endpoint + "/api/v1/difficulty-adjustment")
|
||||
r = await client.get(
|
||||
gerty.mempool_endpoint + "/api/v1/difficulty-adjustment"
|
||||
)
|
||||
text = []
|
||||
stat = round(r.json()['progressPercent'])
|
||||
text.append(get_text_item_dict("Progress through current difficulty epoch", 12))
|
||||
stat = round(r.json()["progressPercent"])
|
||||
text.append(
|
||||
get_text_item_dict("Progress through current difficulty epoch", 12)
|
||||
)
|
||||
text.append(get_text_item_dict("{0}%".format(stat), 20))
|
||||
areas.append(text)
|
||||
|
||||
text = []
|
||||
stat = r.json()['estimatedRetargetDate']
|
||||
stat = r.json()["estimatedRetargetDate"]
|
||||
dt = datetime.fromtimestamp(stat / 1000).strftime("%e %b %Y at %H:%M")
|
||||
text.append(get_text_item_dict("Estimated date of next difficulty adjustment", 12))
|
||||
text.append(
|
||||
get_text_item_dict("Estimated date of next difficulty adjustment", 12)
|
||||
)
|
||||
text.append(get_text_item_dict(dt, 20))
|
||||
areas.append(text)
|
||||
|
||||
text = []
|
||||
stat = r.json()['remainingBlocks']
|
||||
text.append(get_text_item_dict("Blocks remaining until next difficulty adjustment", 12))
|
||||
stat = r.json()["remainingBlocks"]
|
||||
text.append(
|
||||
get_text_item_dict(
|
||||
"Blocks remaining until next difficulty adjustment", 12
|
||||
)
|
||||
)
|
||||
text.append(get_text_item_dict("{0}".format(format_number(stat)), 20))
|
||||
areas.append(text)
|
||||
|
||||
text = []
|
||||
stat = r.json()['remainingTime']
|
||||
text.append(get_text_item_dict("Blocks remaining until next difficulty adjustment", 12))
|
||||
stat = r.json()["remainingTime"]
|
||||
text.append(
|
||||
get_text_item_dict(
|
||||
"Blocks remaining until next difficulty adjustment", 12
|
||||
)
|
||||
)
|
||||
text.append(get_text_item_dict(get_time_remaining(stat / 1000, 4), 20))
|
||||
areas.append(text)
|
||||
|
||||
|
@ -313,8 +337,10 @@ async def get_onchain_dashboard(gerty):
|
|||
async def get_time_remaining_next_difficulty_adjustment(gerty):
|
||||
if isinstance(gerty.mempool_endpoint, str):
|
||||
async with httpx.AsyncClient() as client:
|
||||
r = await client.get(gerty.mempool_endpoint + "/api/v1/difficulty-adjustment")
|
||||
stat = r.json()['remainingTime']
|
||||
r = await client.get(
|
||||
gerty.mempool_endpoint + "/api/v1/difficulty-adjustment"
|
||||
)
|
||||
stat = r.json()["remainingTime"]
|
||||
time = get_time_remaining(stat / 1000, 3)
|
||||
return time
|
||||
|
||||
|
@ -331,17 +357,15 @@ async def get_mempool_stat(stat_slug: str, gerty):
|
|||
text = []
|
||||
if isinstance(gerty.mempool_endpoint, str):
|
||||
async with httpx.AsyncClient() as client:
|
||||
if (
|
||||
stat_slug == "mempool_tx_count"
|
||||
):
|
||||
if stat_slug == "mempool_tx_count":
|
||||
r = await client.get(gerty.mempool_endpoint + "/api/mempool")
|
||||
if stat_slug == "mempool_tx_count":
|
||||
stat = round(r.json()['count'])
|
||||
stat = round(r.json()["count"])
|
||||
text.append(get_text_item_dict("Transactions in the mempool", 15))
|
||||
text.append(get_text_item_dict("{0}".format(format_number(stat)), 80))
|
||||
elif (
|
||||
stat_slug == "mempool_recommended_fees"
|
||||
):
|
||||
text.append(
|
||||
get_text_item_dict("{0}".format(format_number(stat)), 80)
|
||||
)
|
||||
elif stat_slug == "mempool_recommended_fees":
|
||||
y_offset = 60
|
||||
fees = await get_mempool_recommended_fees(gerty)
|
||||
pos_y = 80 + y_offset
|
||||
|
@ -350,35 +374,75 @@ async def get_mempool_stat(stat_slug: str, gerty):
|
|||
text.append(get_text_item_dict("Recommended Tx Fees", 20, 240, pos_y))
|
||||
|
||||
pos_y = 280 + y_offset
|
||||
text.append(get_text_item_dict("{0}".format("No Priority"), 15, 30, pos_y))
|
||||
text.append(get_text_item_dict("{0}".format("Low Priority"), 15, 235, pos_y))
|
||||
text.append(get_text_item_dict("{0}".format("Medium Priority"), 15, 460, pos_y))
|
||||
text.append(get_text_item_dict("{0}".format("High Priority"), 15, 750, pos_y))
|
||||
text.append(
|
||||
get_text_item_dict("{0}".format("No Priority"), 15, 30, pos_y)
|
||||
)
|
||||
text.append(
|
||||
get_text_item_dict("{0}".format("Low Priority"), 15, 235, pos_y)
|
||||
)
|
||||
text.append(
|
||||
get_text_item_dict("{0}".format("Medium Priority"), 15, 460, pos_y)
|
||||
)
|
||||
text.append(
|
||||
get_text_item_dict("{0}".format("High Priority"), 15, 750, pos_y)
|
||||
)
|
||||
|
||||
pos_y = 340 + y_offset
|
||||
font_size = 15
|
||||
fee_append = "/vB"
|
||||
fee_rate = fees["economyFee"]
|
||||
text.append(get_text_item_dict(
|
||||
"{0} {1}{2}".format(format_number(fee_rate), ("sat" if fee_rate == 1 else "sats"), fee_append),
|
||||
text.append(
|
||||
get_text_item_dict(
|
||||
"{0} {1}{2}".format(
|
||||
format_number(fee_rate),
|
||||
("sat" if fee_rate == 1 else "sats"),
|
||||
fee_append,
|
||||
),
|
||||
font_size,
|
||||
30, pos_y))
|
||||
30,
|
||||
pos_y,
|
||||
)
|
||||
)
|
||||
|
||||
fee_rate = fees["hourFee"]
|
||||
text.append(get_text_item_dict(
|
||||
"{0} {1}{2}".format(format_number(fee_rate), ("sat" if fee_rate == 1 else "sats"), fee_append),
|
||||
text.append(
|
||||
get_text_item_dict(
|
||||
"{0} {1}{2}".format(
|
||||
format_number(fee_rate),
|
||||
("sat" if fee_rate == 1 else "sats"),
|
||||
fee_append,
|
||||
),
|
||||
font_size,
|
||||
235, pos_y))
|
||||
235,
|
||||
pos_y,
|
||||
)
|
||||
)
|
||||
|
||||
fee_rate = fees["halfHourFee"]
|
||||
text.append(get_text_item_dict(
|
||||
"{0} {1}{2}".format(format_number(fee_rate), ("sat" if fee_rate == 1 else "sats"), fee_append),
|
||||
text.append(
|
||||
get_text_item_dict(
|
||||
"{0} {1}{2}".format(
|
||||
format_number(fee_rate),
|
||||
("sat" if fee_rate == 1 else "sats"),
|
||||
fee_append,
|
||||
),
|
||||
font_size,
|
||||
460, pos_y))
|
||||
460,
|
||||
pos_y,
|
||||
)
|
||||
)
|
||||
|
||||
fee_rate = fees["fastestFee"]
|
||||
text.append(get_text_item_dict(
|
||||
"{0} {1}{2}".format(format_number(fee_rate), ("sat" if fee_rate == 1 else "sats"), fee_append),
|
||||
text.append(
|
||||
get_text_item_dict(
|
||||
"{0} {1}{2}".format(
|
||||
format_number(fee_rate),
|
||||
("sat" if fee_rate == 1 else "sats"),
|
||||
fee_append,
|
||||
),
|
||||
font_size,
|
||||
750, pos_y))
|
||||
750,
|
||||
pos_y,
|
||||
)
|
||||
)
|
||||
return text
|
||||
|
|
|
@ -3,10 +3,9 @@ import hashlib
|
|||
import pytest
|
||||
|
||||
from lnbits import bolt11
|
||||
from lnbits.core.views.api import (
|
||||
api_payment,
|
||||
)
|
||||
from lnbits.core.views.api import api_payment
|
||||
from lnbits.settings import wallet_class
|
||||
|
||||
from ...helpers import get_random_invoice_data, is_regtest
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import pytest_asyncio
|
||||
|
||||
from lnbits.extensions.boltz.boltz import create_reverse_swap
|
||||
from lnbits.extensions.boltz.models import (
|
||||
CreateReverseSubmarineSwap,
|
||||
)
|
||||
from lnbits.extensions.boltz.models import CreateReverseSubmarineSwap
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="session")
|
||||
|
|
|
@ -4,6 +4,7 @@ from lnbits import bolt11
|
|||
from lnbits.settings import WALLET
|
||||
from lnbits.wallets.base import PaymentResponse, PaymentStatus, StatusResponse
|
||||
from lnbits.wallets.fake import FakeWallet
|
||||
|
||||
from .helpers import get_random_string, is_fake
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue