Endpoints changed but still not working

This commit is contained in:
Ben Arc
2021-10-12 22:34:43 +01:00
parent aeee469c64
commit 1346ad12f1
9 changed files with 181 additions and 203 deletions

View File

@@ -1,16 +1,13 @@
import asyncio import asyncio
from fastapi import APIRouter, FastAPI from fastapi import APIRouter, FastAPI
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from starlette.routing import Mount from starlette.routing import Mount
from lnbits.db import Database from lnbits.db import Database
from lnbits.helpers import template_renderer from lnbits.helpers import template_renderer
from lnbits.tasks import catch_everything_and_restart from lnbits.tasks import catch_everything_and_restart
db = Database("ext_copilot") db = Database("ext_copilot")
copilot_static_files = [ copilot_static_files = [
{ {
"path": "/copilot/static", "path": "/copilot/static",
@@ -18,11 +15,7 @@ copilot_static_files = [
"name": "copilot_static", "name": "copilot_static",
} }
] ]
copilot_ext: APIRouter = APIRouter( copilot_ext: APIRouter = APIRouter(prefix="/copilot", tags=["copilot"])
prefix="/copilot",
tags=["copilot"]
# "lnurlp", __name__, static_folder="static", template_folder="templates"
)
def copilot_renderer(): def copilot_renderer():

View File

@@ -1,36 +1,14 @@
from typing import List, Optional, Union from typing import List, Optional, Union
# from lnbits.db import open_ext_db
from . import db from . import db
from .models import Copilots from .models import Copilots, CreateCopilotData
from lnbits.helpers import urlsafe_short_hash from lnbits.helpers import urlsafe_short_hash
from quart import jsonify
###############COPILOTS########################## ###############COPILOTS##########################
async def create_copilot( async def create_copilot(
title: str, data: CreateCopilotData, inkey: Optional[str] = ""
user: str,
lnurl_toggle: Optional[int] = 0,
wallet: Optional[str] = None,
animation1: Optional[str] = None,
animation2: Optional[str] = None,
animation3: Optional[str] = None,
animation1threshold: Optional[int] = None,
animation2threshold: Optional[int] = None,
animation3threshold: Optional[int] = None,
animation1webhook: Optional[str] = None,
animation2webhook: Optional[str] = None,
animation3webhook: Optional[str] = None,
lnurl_title: Optional[str] = None,
show_message: Optional[int] = 0,
show_ack: Optional[int] = 0,
show_price: Optional[str] = None,
amount_made: Optional[int] = None,
) -> Copilots: ) -> Copilots:
copilot_id = urlsafe_short_hash() copilot_id = urlsafe_short_hash()
@@ -60,24 +38,24 @@ async def create_copilot(
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", """,
( (
copilot_id, data.copilot_id,
user, data.user,
int(lnurl_toggle), int(data.lnurl_toggle),
wallet, data.wallet,
title, data.title,
animation1, data.animation1,
animation2, data.animation2,
animation3, data.animation3,
animation1threshold, data.animation1threshold,
animation2threshold, data.animation2threshold,
animation3threshold, data.animation3threshold,
animation1webhook, data.animation1webhook,
animation2webhook, data.animation2webhook,
animation3webhook, data.animation3webhook,
lnurl_title, data.lnurl_title,
int(show_message), int(data.show_message),
int(show_ack), int(data.show_ack),
show_price, data.show_price,
0, 0,
), ),
) )
@@ -89,17 +67,23 @@ async def update_copilot(copilot_id: str, **kwargs) -> Optional[Copilots]:
await db.execute( await db.execute(
f"UPDATE copilot.copilots SET {q} WHERE id = ?", (*kwargs.values(), copilot_id) f"UPDATE copilot.copilots SET {q} WHERE id = ?", (*kwargs.values(), copilot_id)
) )
row = await db.fetchone("SELECT * FROM copilot.copilots WHERE id = ?", (copilot_id,)) row = await db.fetchone(
"SELECT * FROM copilot.copilots WHERE id = ?", (copilot_id,)
)
return Copilots.from_row(row) if row else None return Copilots.from_row(row) if row else None
async def get_copilot(copilot_id: str) -> Copilots: async def get_copilot(copilot_id: str) -> Copilots:
row = await db.fetchone("SELECT * FROM copilot.copilots WHERE id = ?", (copilot_id,)) row = await db.fetchone(
"SELECT * FROM copilot.copilots WHERE id = ?", (copilot_id,)
)
return Copilots.from_row(row) if row else None return Copilots.from_row(row) if row else None
async def get_copilots(user: str) -> List[Copilots]: async def get_copilots(user: str) -> List[Copilots]:
rows = await db.fetchall("""SELECT * FROM copilot.copilots WHERE "user" = ?""", (user,)) rows = await db.fetchall(
"""SELECT * FROM copilot.copilots WHERE "user" = ?""", (user,)
)
return [Copilots.from_row(row) for row in rows] return [Copilots.from_row(row) for row in rows]

View File

@@ -1,23 +1,36 @@
import json import json
import hashlib import hashlib
import math import math
from quart import jsonify, url_for, request from fastapi import Request
import hashlib
from http import HTTPStatus
from starlette.exceptions import HTTPException
from starlette.responses import HTMLResponse, JSONResponse # type: ignore
import base64
from lnurl import LnurlPayResponse, LnurlPayActionResponse, LnurlErrorResponse # type: ignore from lnurl import LnurlPayResponse, LnurlPayActionResponse, LnurlErrorResponse # type: ignore
from lnurl.types import LnurlPayMetadata from lnurl.types import LnurlPayMetadata
from lnbits.core.services import create_invoice from lnbits.core.services import create_invoice
from .models import Copilots, CreateCopilotData
from . import copilot_ext from . import copilot_ext
from .crud import get_copilot from .crud import get_copilot
from typing import Optional
from fastapi.params import Depends
from fastapi.param_functions import Query
from .models import CreateJukeLinkData, CreateJukeboxPayment
@copilot_ext.route("/lnurl/<cp_id>", methods=["GET"]) @copilot_ext.get("/lnurl/{cp_id}", response_class=HTMLResponse)
async def lnurl_response(cp_id): async def lnurl_response(req: Request, cp_id: str = Query(None)):
cp = await get_copilot(cp_id) cp = await get_copilot(cp_id)
if not cp: if not cp:
return jsonify({"status": "ERROR", "reason": "Copilot not found."}) raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Copilot not found",
)
resp = LnurlPayResponse( resp = LnurlPayResponse(
callback=url_for("copilot.lnurl_callback", cp_id=cp_id, _external=True), callback=req.url_for("copilot.lnurl_callback", cp_id=cp_id, _external=True),
min_sendable=10000, min_sendable=10000,
max_sendable=50000000, max_sendable=50000000,
metadata=LnurlPayMetadata(json.dumps([["text/plain", str(cp.lnurl_title)]])), metadata=LnurlPayMetadata(json.dumps([["text/plain", str(cp.lnurl_title)]])),
@@ -27,42 +40,36 @@ async def lnurl_response(cp_id):
if cp.show_message: if cp.show_message:
params["commentAllowed"] = 300 params["commentAllowed"] = 300
return jsonify(params) return params
@copilot_ext.route("/lnurl/cb/<cp_id>", methods=["GET"]) @copilot_ext.get("/lnurl/cb/{cp_id}", response_class=HTMLResponse)
async def lnurl_callback(cp_id): async def lnurl_callback(
cp_id: str = Query(None), amount: str = Query(None), comment: str = Query(None)
):
cp = await get_copilot(cp_id) cp = await get_copilot(cp_id)
if not cp: if not cp:
return jsonify({"status": "ERROR", "reason": "Copilot not found."}) raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Copilot not found",
)
amount_received = int(request.args.get("amount")) amount_received = int(amount)
if amount_received < 10000: if amount_received < 10000:
return ( return LnurlErrorResponse(
jsonify( reason=f"Amount {round(amount_received / 1000)} is smaller than minimum 10 sats."
LnurlErrorResponse( ).dict()
reason=f"Amount {round(amount_received / 1000)} is smaller than minimum 10 sats."
).dict()
),
)
elif amount_received / 1000 > 10000000: elif amount_received / 1000 > 10000000:
return ( return LnurlErrorResponse(
jsonify( reason=f"Amount {round(amount_received / 1000)} is greater than maximum 50000."
LnurlErrorResponse( ).dict()
reason=f"Amount {round(amount_received / 1000)} is greater than maximum 50000."
).dict()
),
)
comment = "" comment = ""
if request.args.get("comment"): if comment:
comment = request.args.get("comment")
if len(comment or "") > 300: if len(comment or "") > 300:
return jsonify( return LnurlErrorResponse(
LnurlErrorResponse( reason=f"Got a comment with {len(comment)} characters, but can only accept 300"
reason=f"Got a comment with {len(comment)} characters, but can only accept 300" ).dict()
).dict()
)
if len(comment) < 1: if len(comment) < 1:
comment = "none" comment = "none"
@@ -83,4 +90,4 @@ async def lnurl_callback(cp_id):
disposable=False, disposable=False,
routes=[], routes=[],
) )
return jsonify(resp.dict()) return resp.dict()

View File

@@ -9,7 +9,7 @@ from sqlite3 import Row
from pydantic import BaseModel from pydantic import BaseModel
class CreateCopilots(BaseModel): class CreateCopilotData(BaseModel):
id: str = Query(None) id: str = Query(None)
user: str = Query(None) user: str = Query(None)
title: str = Query(None) title: str = Query(None)

View File

@@ -1,8 +1,6 @@
import trio # type: ignore import asyncio
import json import json
import httpx import httpx
from quart import g, jsonify, url_for, websocket
from http import HTTPStatus
from lnbits.core import db as core_db from lnbits.core import db as core_db
from lnbits.core.models import Payment from lnbits.core.models import Payment
@@ -11,16 +9,17 @@ from lnbits.tasks import register_invoice_listener
from .crud import get_copilot from .crud import get_copilot
from .views import updater from .views import updater
import shortuuid import shortuuid
from http import HTTPStatus
from starlette.exceptions import HTTPException
from starlette.responses import HTMLResponse, JSONResponse # type: ignore
async def register_listeners(): async def wait_for_paid_invoices():
invoice_paid_chan_send, invoice_paid_chan_recv = trio.open_memory_channel(2) invoice_queue = asyncio.Queue()
register_invoice_listener(invoice_paid_chan_send) register_invoice_listener(invoice_queue)
await wait_for_paid_invoices(invoice_paid_chan_recv)
while True:
async def wait_for_paid_invoices(invoice_paid_chan: trio.MemoryReceiveChannel): payment = await invoice_queue.get()
async for payment in invoice_paid_chan:
await on_invoice_paid(payment) await on_invoice_paid(payment)
@@ -38,9 +37,9 @@ async def on_invoice_paid(payment: Payment) -> None:
copilot = await get_copilot(payment.extra.get("copilot", -1)) copilot = await get_copilot(payment.extra.get("copilot", -1))
if not copilot: if not copilot:
return ( raise HTTPException(
jsonify({"message": "Copilot link link does not exist."}), status_code=HTTPStatus.NOT_FOUND,
HTTPStatus.NOT_FOUND, detail="Copilot does not exist",
) )
if copilot.animation1threshold: if copilot.animation1threshold:
if int(payment.amount / 1000) >= copilot.animation1threshold: if int(payment.amount / 1000) >= copilot.animation1threshold:
@@ -74,15 +73,3 @@ async def on_invoice_paid(payment: Payment) -> None:
await updater(copilot.id, data, payment.extra.get("comment")) await updater(copilot.id, data, payment.extra.get("comment"))
else: else:
await updater(copilot.id, data, "none") await updater(copilot.id, data, "none")
async def mark_webhook_sent(payment: Payment, status: int) -> None:
payment.extra["wh_status"] = status
await core_db.execute(
"""
UPDATE apipayments SET extra = ?
WHERE hash = ?
""",
(json.dumps(payment.extra), payment.payment_hash),
)

View File

@@ -1,8 +1,8 @@
from http import HTTPStatus from http import HTTPStatus
import httpx import httpx
from collections import defaultdict from collections import defaultdict
from lnbits.decorators import check_user_exists, validate_uuids from lnbits.decorators import check_user_exists
import asyncio
from .crud import get_copilot from .crud import get_copilot
from functools import wraps from functools import wraps
@@ -10,13 +10,15 @@ from functools import wraps
from lnbits.decorators import check_user_exists from lnbits.decorators import check_user_exists
from . import copilot_ext, copilot_renderer from . import copilot_ext, copilot_renderer
from fastapi import FastAPI, Request from fastapi import FastAPI, Request, WebSocket
from fastapi.params import Depends from fastapi.params import Depends
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
from fastapi.param_functions import Query
from starlette.exceptions import HTTPException from starlette.exceptions import HTTPException
from starlette.responses import HTMLResponse from starlette.responses import HTMLResponse, JSONResponse # type: ignore
from lnbits.core.models import User from lnbits.core.models import User
import base64
templates = Jinja2Templates(directory="templates") templates = Jinja2Templates(directory="templates")
@@ -50,25 +52,25 @@ async def panel(request: Request):
connected_websockets = defaultdict(set) connected_websockets = defaultdict(set)
@copilot_ext.websocket("/ws/<id>/") # @copilot_ext.websocket("/ws/{id}/")
async def wss(id): # async def websocket_endpoint(websocket: WebSocket, id: str = Query(None)):
copilot = await get_copilot(id) # copilot = await get_copilot(id)
if not copilot: # if not copilot:
return "", HTTPStatus.FORBIDDEN # return "", HTTPStatus.FORBIDDEN
global connected_websockets # await websocket.accept()
send_channel, receive_channel = trio.open_memory_channel(0) # invoice_queue = asyncio.Queue()
connected_websockets[id].add(send_channel) # connected_websockets[id].add(invoice_queue)
try: # try:
while True: # while True:
data = await receive_channel.receive() # data = await websocket.receive_text()
await websocket.send(data) # await websocket.send_text(f"Message text was: {data}")
finally: # finally:
connected_websockets[id].remove(send_channel) # connected_websockets[id].remove(invoice_queue)
async def updater(copilot_id, data, comment): # async def updater(copilot_id, data, comment):
copilot = await get_copilot(copilot_id) # copilot = await get_copilot(copilot_id)
if not copilot: # if not copilot:
return # return
for queue in connected_websockets[copilot_id]: # for queue in connected_websockets[copilot_id]:
await queue.send(f"{data + '-' + comment}") # await queue.send(f"{data + '-' + comment}")

View File

@@ -1,15 +1,29 @@
from fastapi import Request
import hashlib import hashlib
from quart import g, jsonify, url_for, websocket
from http import HTTPStatus from http import HTTPStatus
import httpx from starlette.exceptions import HTTPException
from starlette.responses import HTMLResponse, JSONResponse # type: ignore
import base64
from lnbits.core.crud import get_user from lnbits.core.crud import get_user
from lnbits.decorators import api_check_wallet_key, api_validate_post_request from lnbits.core.services import create_invoice, check_invoice_status
import json
from typing import Optional
from fastapi.params import Depends
from fastapi.param_functions import Query
from .models import Copilots, CreateCopilotData
from lnbits.decorators import (
WalletAdminKeyChecker,
WalletInvoiceKeyChecker,
api_validate_post_request,
check_user_exists,
WalletTypeInfo,
get_key_type,
api_validate_post_request,
)
from .views import updater from .views import updater
import httpx
from . import copilot_ext from . import copilot_ext
from lnbits.extensions.copilot import copilot_ext
from .crud import ( from .crud import (
create_copilot, create_copilot,
update_copilot, update_copilot,
@@ -21,89 +35,81 @@ from .crud import (
#######################COPILOT########################## #######################COPILOT##########################
@copilot_ext.route("/api/v1/copilot", methods=["POST"]) @copilot_ext.post("/api/v1/copilot", response_class=HTMLResponse)
@copilot_ext.route("/api/v1/copilot/<copilot_id>", methods=["PUT"]) @copilot_ext.put("/api/v1/copilot/{juke_id}", response_class=HTMLResponse)
@api_check_wallet_key("admin") async def api_copilot_create_or_update(
@api_validate_post_request( data: CreateCopilotData,
schema={ copilot_id: str = Query(None),
"title": {"type": "string", "empty": False, "required": True}, wallet: WalletTypeInfo = Depends(get_key_type),
"lnurl_toggle": {"type": "integer", "empty": False}, ):
"wallet": {"type": "string", "empty": False, "required": False},
"animation1": {"type": "string", "empty": True, "required": False},
"animation2": {"type": "string", "empty": True, "required": False},
"animation3": {"type": "string", "empty": True, "required": False},
"animation1threshold": {"type": "integer", "empty": True, "required": False},
"animation2threshold": {"type": "integer", "empty": True, "required": False},
"animation3threshold": {"type": "integer", "empty": True, "required": False},
"animation1webhook": {"type": "string", "empty": True, "required": False},
"animation2webhook": {"type": "string", "empty": True, "required": False},
"animation3webhook": {"type": "string", "empty": True, "required": False},
"lnurl_title": {"type": "string", "empty": True, "required": False},
"show_message": {"type": "integer", "empty": True, "required": False},
"show_ack": {"type": "integer", "empty": True},
"show_price": {"type": "string", "empty": True},
}
)
async def api_copilot_create_or_update(copilot_id=None):
if not copilot_id: if not copilot_id:
copilot = await create_copilot(user=g.wallet.user, **g.data) copilot = await create_copilot(data, user=wallet.wallet.user)
return jsonify(copilot._asdict()), HTTPStatus.CREATED return copilot, HTTPStatus.CREATED
else: else:
copilot = await update_copilot(copilot_id=copilot_id, **g.data) copilot = await update_copilot(data, copilot_id=copilot_id)
return jsonify(copilot._asdict()), HTTPStatus.OK return copilot
@copilot_ext.route("/api/v1/copilot", methods=["GET"]) @copilot_ext.get("/api/v1/copilot", response_class=HTMLResponse)
@api_check_wallet_key("invoice") async def api_copilots_retrieve(wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_copilots_retrieve():
try: try:
return ( return [{copilot} for copilot in await get_copilots(wallet.wallet.user)]
jsonify(
[{**copilot._asdict()} for copilot in await get_copilots(g.wallet.user)]
),
HTTPStatus.OK,
)
except: except:
return "" return ""
@copilot_ext.route("/api/v1/copilot/<copilot_id>", methods=["GET"]) @copilot_ext.get("/api/v1/copilot/{copilot_id}", response_class=HTMLResponse)
@api_check_wallet_key("invoice") async def api_copilot_retrieve(
async def api_copilot_retrieve(copilot_id): copilot_id: str = Query(None), wallet: WalletTypeInfo = Depends(get_key_type)
):
copilot = await get_copilot(copilot_id) copilot = await get_copilot(copilot_id)
if not copilot: if not copilot:
return jsonify({"message": "copilot does not exist"}), HTTPStatus.NOT_FOUND raise HTTPException(
if not copilot.lnurl_toggle: status_code=HTTPStatus.NOT_FOUND,
return ( detail="Copilot not found",
jsonify({**copilot._asdict()}),
HTTPStatus.OK,
) )
return ( if not copilot.lnurl_toggle:
jsonify({**copilot._asdict(), **{"lnurl": copilot.lnurl}}), return copilot.dict()
HTTPStatus.OK, return {**copilot.dict(), **{"lnurl": copilot.lnurl}}
)
@copilot_ext.route("/api/v1/copilot/<copilot_id>", methods=["DELETE"]) @copilot_ext.delete("/api/v1/copilot/{copilot_id}", response_class=HTMLResponse)
@api_check_wallet_key("admin") async def api_copilot_delete(
async def api_copilot_delete(copilot_id): copilot_id: str = Query(None),
wallet: WalletTypeInfo = Depends(WalletAdminKeyChecker()),
):
copilot = await get_copilot(copilot_id) copilot = await get_copilot(copilot_id)
if not copilot: if not copilot:
return jsonify({"message": "Wallet link does not exist."}), HTTPStatus.NOT_FOUND raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Copilot does not exist",
)
await delete_copilot(copilot_id) await delete_copilot(copilot_id)
return "", HTTPStatus.NO_CONTENT return "", HTTPStatus.NO_CONTENT
@copilot_ext.route("/api/v1/copilot/ws/<copilot_id>/<comment>/<data>", methods=["GET"]) @copilot_ext.get(
async def api_copilot_ws_relay(copilot_id, comment, data): "/api/v1/copilot/ws/{copilot_id}/{comment}/{data}", response_class=HTMLResponse
)
async def api_copilot_ws_relay(
copilot_id: str = Query(None),
comment: str = Query(None),
data: str = Query(None),
):
copilot = await get_copilot(copilot_id) copilot = await get_copilot(copilot_id)
if not copilot: if not copilot:
return jsonify({"message": "copilot does not exist"}), HTTPStatus.NOT_FOUND raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Copilot does not exist",
)
try: try:
await updater(copilot_id, data, comment) await updater(copilot_id, data, comment)
except: except:
return "", HTTPStatus.FORBIDDEN raise HTTPException(
return "", HTTPStatus.OK status_code=HTTPStatus.FORBIDDEN,
detail="Not your copilot",
)
return ""

View File

@@ -1,9 +1,7 @@
import asyncio import asyncio
from fastapi import APIRouter, FastAPI from fastapi import APIRouter, FastAPI
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from starlette.routing import Mount from starlette.routing import Mount
from lnbits.db import Database from lnbits.db import Database
from lnbits.helpers import template_renderer from lnbits.helpers import template_renderer
from lnbits.tasks import catch_everything_and_restart from lnbits.tasks import catch_everything_and_restart

View File

@@ -1,5 +1,6 @@
import json import json
import time import time
from datetime import datetime from datetime import datetime
from http import HTTPStatus from http import HTTPStatus
from lnbits.decorators import check_user_exists, WalletTypeInfo, get_key_type from lnbits.decorators import check_user_exists, WalletTypeInfo, get_key_type