lnbits-legend/lnbits/extensions/gerty/views_api.py

453 lines
15 KiB
Python
Raw Normal View History

2022-09-23 14:38:55 +01:00
import json
2022-10-20 16:31:09 +01:00
import math
2022-09-23 14:38:55 +01:00
import os
2022-10-20 16:31:09 +01:00
import random
2022-09-29 16:06:45 +01:00
import time
from datetime import datetime
2022-10-20 16:31:09 +01:00
from http import HTTPStatus
import httpx
2022-09-23 14:38:55 +01:00
from fastapi import Query
from fastapi.params import Depends
2022-10-20 16:31:09 +01:00
from fastapi.templating import Jinja2Templates
2022-09-23 14:38:55 +01:00
from lnurl import decode as decode_lnurl
from loguru import logger
from starlette.exceptions import HTTPException
2022-10-20 16:31:09 +01:00
from lnbits.core.crud import get_user, get_wallet_for_key
2022-09-23 14:38:55 +01:00
from lnbits.core.services import create_invoice
2022-09-26 16:16:41 +01:00
from lnbits.core.views.api import api_payment, api_wallet
2022-09-23 14:38:55 +01:00
from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
2022-10-20 16:31:09 +01:00
from lnbits.utils.exchange_rates import satoshis_amount_as_fiat
2022-10-20 16:31:09 +01:00
from ...settings import LNBITS_PATH
2022-09-23 14:38:55 +01:00
from . import gerty_ext
2022-10-20 16:31:09 +01:00
from .crud import create_gerty, delete_gerty, get_gerty, get_gertys, update_gerty
from .helpers import *
2022-09-23 14:38:55 +01:00
from .models import Gerty
@gerty_ext.get("/api/v1/gerty", status_code=HTTPStatus.OK)
async def api_gertys(
2022-10-20 16:31:09 +01:00
all_wallets: bool = Query(False), wallet: WalletTypeInfo = Depends(get_key_type)
2022-09-23 14:38:55 +01:00
):
wallet_ids = [wallet.wallet.id]
if all_wallets:
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
return [gerty.dict() for gerty in await get_gertys(wallet_ids)]
@gerty_ext.post("/api/v1/gerty", status_code=HTTPStatus.CREATED)
@gerty_ext.put("/api/v1/gerty/{gerty_id}", status_code=HTTPStatus.OK)
async def api_link_create_or_update(
2022-10-20 16:31:09 +01:00
data: Gerty,
wallet: WalletTypeInfo = Depends(get_key_type),
gerty_id: str = Query(None),
2022-09-23 14:38:55 +01:00
):
if gerty_id:
gerty = await get_gerty(gerty_id)
if not gerty:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Gerty does not exist"
)
if gerty.wallet != wallet.wallet.id:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="Come on, seriously, this isn't your Gerty!",
)
data.wallet = wallet.wallet.id
gerty = await update_gerty(gerty_id, **data.dict())
else:
gerty = await create_gerty(wallet_id=wallet.wallet.id, data=data)
return {**gerty.dict()}
2022-09-29 16:06:45 +01:00
2022-09-23 14:38:55 +01:00
@gerty_ext.delete("/api/v1/gerty/{gerty_id}")
async def api_gerty_delete(
2022-10-20 16:31:09 +01:00
gerty_id: str, wallet: WalletTypeInfo = Depends(require_admin_key)
2022-09-23 14:38:55 +01:00
):
gerty = await get_gerty(gerty_id)
if not gerty:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Gerty does not exist."
)
if gerty.wallet != wallet.wallet.id:
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not your Gerty.")
await delete_gerty(gerty_id)
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
#######################
2022-10-20 16:31:09 +01:00
2022-09-23 14:38:55 +01:00
@gerty_ext.get("/api/v1/gerty/satoshiquote", status_code=HTTPStatus.OK)
async def api_gerty_satoshi():
2022-10-20 16:31:09 +01:00
maxQuoteLength = 186
with open(os.path.join(LNBITS_PATH, "extensions/gerty/static/satoshi.json")) as fd:
2022-09-29 16:44:56 +01:00
satoshiQuotes = json.load(fd)
quote = satoshiQuotes[random.randint(0, len(satoshiQuotes) - 1)]
# logger.debug(quote.text)
if len(quote["text"]) > maxQuoteLength:
logger.debug("Quote is too long, getting another")
return await api_gerty_satoshi()
else:
return quote
2022-09-29 16:06:45 +01:00
2022-09-29 17:53:53 +01:00
@gerty_ext.get("/api/v1/gerty/{gerty_id}/{p}")
2022-10-20 16:31:09 +01:00
async def api_gerty_json(gerty_id: str, p: int = None): # page number
2022-09-23 14:38:55 +01:00
gerty = await get_gerty(gerty_id)
2022-09-29 16:06:45 +01:00
2022-09-23 14:38:55 +01:00
if not gerty:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Gerty does not exist."
)
2022-09-26 18:06:09 +01:00
2022-09-29 16:06:45 +01:00
display_preferences = json.loads(gerty.display_preferences)
enabled_screen_count = 0
2022-09-26 18:06:09 +01:00
2022-09-29 16:06:45 +01:00
enabled_screens = []
for screen_slug in display_preferences:
is_screen_enabled = display_preferences[screen_slug]
if is_screen_enabled:
enabled_screen_count += 1
enabled_screens.append(screen_slug)
2022-10-03 17:18:53 +01:00
logger.debug("Screeens " + str(enabled_screens))
2022-10-06 16:12:48 +01:00
data = await get_screen_data(p, enabled_screens, gerty)
2022-09-29 16:06:45 +01:00
2022-10-20 16:31:09 +01:00
next_screen_number = 0 if ((p + 1) >= enabled_screen_count) else p + 1
2022-09-29 15:08:01 +01:00
2022-10-07 10:25:54 +01:00
# get the sleep time
sleep_time = gerty.refresh_time if gerty.refresh_time else 300
2022-10-20 17:15:12 +01:00
utc_offset = gerty.utc_offset if gerty.utc_offset else 0
if gerty_should_sleep(utc_offset):
2022-10-07 22:12:48 +01:00
sleep_time_hours = 8
sleep_time = 60 * 60 * sleep_time_hours
2022-09-29 15:08:01 +01:00
2022-09-29 16:06:45 +01:00
return {
"settings": {
2022-10-07 10:25:54 +01:00
"refreshTime": sleep_time,
2022-10-20 17:15:12 +01:00
"requestTimestamp": get_next_update_time(sleep_time, utc_offset),
2022-09-29 16:06:45 +01:00
"nextScreenNumber": next_screen_number,
2022-09-30 09:17:20 +01:00
"showTextBoundRect": False,
2022-10-20 16:31:09 +01:00
"name": gerty.name,
2022-09-29 16:06:45 +01:00
},
"screen": {
"slug": get_screen_slug_by_index(p, enabled_screens),
"group": get_screen_slug_by_index(p, enabled_screens),
2022-10-20 16:31:09 +01:00
"title": data["title"],
"areas": data["areas"],
},
2022-09-29 16:06:45 +01:00
}
2022-09-29 16:34:47 +01:00
# Get a screen slug by its position in the screens_list
def get_screen_slug_by_index(index: int, screens_list):
if(index < len(screens_list) - 1):
return list(screens_list)[index]
else:
return None
2022-09-29 16:34:47 +01:00
# Get a list of text items for the screen number
2022-10-06 16:12:48 +01:00
async def get_screen_data(screen_num: int, screens_list: dict, gerty):
screen_slug = get_screen_slug_by_index(screen_num, screens_list)
2022-09-29 16:06:45 +01:00
# first get the relevant slug from the display_preferences
2022-10-20 16:31:09 +01:00
logger.debug("screen_slug")
logger.debug(screen_slug)
areas = []
2022-10-06 16:12:48 +01:00
title = ""
2022-10-03 17:18:53 +01:00
if screen_slug == "dashboard":
2022-10-06 17:48:49 +01:00
title = gerty.name
2022-10-04 10:52:53 +01:00
areas = await get_dashboard(gerty)
elif screen_slug == "fun_satoshi_quotes":
areas.append(await get_satoshi_quotes())
elif screen_slug == "fun_exchange_market_rate":
areas.append(await get_exchange_rate(gerty))
elif screen_slug == "onchain_dashboard":
2022-10-20 15:59:55 +01:00
title = "Onchain Data"
areas = await get_onchain_dashboard(gerty)
elif screen_slug == "mempool_recommended_fees":
2022-10-06 11:05:49 +01:00
areas.append(await get_mempool_stat(screen_slug, gerty))
2022-10-20 15:59:55 +01:00
elif screen_slug == "mining_dashboard":
title = "Mining Data"
areas = await get_mining_dashboard(gerty)
2022-10-06 16:12:48 +01:00
elif screen_slug == "lightning_dashboard":
title = "Lightning Network"
areas = await get_lightning_stats(gerty)
data = {}
2022-10-20 16:31:09 +01:00
data["title"] = title
data["areas"] = areas
2022-10-06 16:12:48 +01:00
return data
2022-10-03 17:18:53 +01:00
# Get the dashboard screen
async def get_dashboard(gerty):
areas = []
2022-10-03 17:18:53 +01:00
# XC rate
2022-10-04 10:52:53 +01:00
text = []
amount = await satoshis_amount_as_fiat(100000000, gerty.exchange)
text.append(get_text_item_dict(format_number(amount), 40))
text.append(get_text_item_dict("BTC{0} price".format(gerty.exchange), 15))
areas.append(text)
2022-10-03 17:18:53 +01:00
# balance
2022-10-04 10:52:53 +01:00
text = []
2022-10-06 17:06:22 +01:00
wallets = await get_lnbits_wallet_balances(gerty)
text = []
for wallet in wallets:
2022-10-20 16:31:09 +01:00
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)
2022-10-03 17:18:53 +01:00
# Mempool fees
2022-10-04 10:52:53 +01:00
text = []
text.append(get_text_item_dict(format_number(await get_block_height(gerty)), 40))
text.append(get_text_item_dict("Current block height", 15))
areas.append(text)
2022-10-03 17:18:53 +01:00
# difficulty adjustment time
2022-10-04 10:52:53 +01:00
text = []
2022-10-20 16:31:09 +01:00
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)
2022-10-03 17:18:53 +01:00
return areas
2022-10-03 17:18:53 +01:00
async def get_lnbits_wallet_balances(gerty):
2022-09-29 16:06:45 +01:00
# Get Wallet info
wallets = []
if gerty.lnbits_wallets != "":
for lnbits_wallet in json.loads(gerty.lnbits_wallets):
wallet = await get_wallet_for_key(key=lnbits_wallet)
2022-10-06 17:06:22 +01:00
logger.debug(wallet.name)
2022-09-29 16:06:45 +01:00
if wallet:
2022-10-20 16:31:09 +01:00
wallets.append(
{
"name": wallet.name,
"balance": wallet.balance_msat / 1000,
"inkey": wallet.inkey,
}
)
2022-10-06 17:06:22 +01:00
return wallets
2022-09-23 14:38:55 +01:00
async def get_placeholder_text():
return [
2022-09-29 19:28:50 +01:00
get_text_item_dict("Some placeholder text", 15, 10, 50),
2022-10-20 16:31:09 +01:00
get_text_item_dict("Some placeholder text", 15, 10, 50),
]
2022-09-29 16:34:47 +01:00
2022-09-29 16:34:47 +01:00
async def get_satoshi_quotes():
# Get Satoshi quotes
text = []
quote = await api_gerty_satoshi()
if quote:
2022-10-20 16:31:09 +01:00
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)
)
2022-09-29 16:44:56 +01:00
return text
2022-09-29 16:34:47 +01:00
2022-09-29 16:54:37 +01:00
# Get Exchange Value
async def get_exchange_rate(gerty):
text = []
if gerty.exchange != "":
try:
amount = await satoshis_amount_as_fiat(100000000, gerty.exchange)
if amount:
2022-10-02 10:20:05 +01:00
price = format_number(amount)
2022-10-20 16:31:09 +01:00
text.append(
get_text_item_dict(
"Current {0}/BTC price".format(gerty.exchange), 15
)
)
2022-09-29 21:37:12 +01:00
text.append(get_text_item_dict(price, 80))
2022-09-29 16:54:37 +01:00
except:
pass
return text
async def get_onchain_dashboard(gerty):
areas = []
if isinstance(gerty.mempool_endpoint, str):
async with httpx.AsyncClient() as client:
2022-10-20 16:31:09 +01:00
r = await client.get(
gerty.mempool_endpoint + "/api/v1/difficulty-adjustment"
)
text = []
2022-10-20 16:31:09 +01:00
stat = round(r.json()["progressPercent"])
text.append(
get_text_item_dict("Progress through current epoch", 12)
2022-10-20 16:31:09 +01:00
)
text.append(get_text_item_dict("{0}%".format(stat), 60))
areas.append(text)
text = []
2022-10-20 16:31:09 +01:00
stat = r.json()["estimatedRetargetDate"]
dt = datetime.fromtimestamp(stat / 1000).strftime("%e %b %Y at %H:%M")
2022-10-20 16:31:09 +01:00
text.append(
get_text_item_dict("Date of next difficulty adjustment", 12)
2022-10-20 16:31:09 +01:00
)
text.append(get_text_item_dict(dt, 20))
areas.append(text)
text = []
2022-10-20 16:31:09 +01:00
stat = r.json()["remainingBlocks"]
text.append(
get_text_item_dict(
"Blocks until next adjustment", 12
2022-10-20 16:31:09 +01:00
)
)
text.append(get_text_item_dict("{0}".format(format_number(stat)), 60))
areas.append(text)
text = []
2022-10-20 16:31:09 +01:00
stat = r.json()["remainingTime"]
text.append(
get_text_item_dict(
"Blocks until next adjustment", 12
2022-10-20 16:31:09 +01:00
)
)
text.append(get_text_item_dict(get_time_remaining(stat / 1000, 4), 60))
areas.append(text)
return areas
2022-10-04 10:52:53 +01:00
2022-10-20 16:14:45 +01:00
2022-10-04 10:52:53 +01:00
async def get_time_remaining_next_difficulty_adjustment(gerty):
if isinstance(gerty.mempool_endpoint, str):
async with httpx.AsyncClient() as client:
2022-10-20 16:31:09 +01:00
r = await client.get(
gerty.mempool_endpoint + "/api/v1/difficulty-adjustment"
)
stat = r.json()["remainingTime"]
2022-10-04 10:52:53 +01:00
time = get_time_remaining(stat / 1000, 3)
return time
2022-10-04 10:52:53 +01:00
async def get_block_height(gerty):
if isinstance(gerty.mempool_endpoint, str):
async with httpx.AsyncClient() as client:
r = await client.get(gerty.mempool_endpoint + "/api/blocks/tip/height")
return r.json()
2022-09-29 17:38:51 +01:00
async def get_mempool_stat(stat_slug: str, gerty):
text = []
if isinstance(gerty.mempool_endpoint, str):
async with httpx.AsyncClient() as client:
2022-10-20 16:31:09 +01:00
if stat_slug == "mempool_tx_count":
2022-09-29 17:38:51 +01:00
r = await client.get(gerty.mempool_endpoint + "/api/mempool")
if stat_slug == "mempool_tx_count":
2022-10-20 16:31:09 +01:00
stat = round(r.json()["count"])
2022-09-29 19:28:50 +01:00
text.append(get_text_item_dict("Transactions in the mempool", 15))
2022-10-20 16:31:09 +01:00
text.append(
get_text_item_dict("{0}".format(format_number(stat)), 80)
)
elif stat_slug == "mempool_recommended_fees":
2022-10-06 12:08:46 +01:00
y_offset = 60
2022-10-06 11:05:49 +01:00
fees = await get_mempool_recommended_fees(gerty)
2022-10-06 12:08:46 +01:00
pos_y = 80 + y_offset
text.append(get_text_item_dict("mempool.space", 40, 160, pos_y))
pos_y = 180 + y_offset
text.append(get_text_item_dict("Recommended Tx Fees", 20, 240, pos_y))
pos_y = 280 + y_offset
2022-10-20 16:31:09 +01:00
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)
)
2022-10-06 12:08:46 +01:00
pos_y = 340 + y_offset
font_size = 15
fee_append = "/vB"
fee_rate = fees["economyFee"]
2022-10-20 16:31:09 +01:00
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,
)
)
2022-10-06 12:08:46 +01:00
fee_rate = fees["hourFee"]
2022-10-20 16:31:09 +01:00
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,
)
)
2022-10-06 12:08:46 +01:00
fee_rate = fees["halfHourFee"]
2022-10-20 16:31:09 +01:00
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,
)
)
2022-10-06 12:08:46 +01:00
fee_rate = fees["fastestFee"]
2022-10-20 16:31:09 +01:00
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,
)
)
2022-10-20 16:14:45 +01:00
return text