|
|
|
@ -6,6 +6,7 @@ import asyncio |
|
|
|
import json |
|
|
|
import json |
|
|
|
from typing import Callable, Optional, NamedTuple, Any, TYPE_CHECKING |
|
|
|
from typing import Callable, Optional, NamedTuple, Any, TYPE_CHECKING |
|
|
|
import re |
|
|
|
import re |
|
|
|
|
|
|
|
import urllib.parse |
|
|
|
|
|
|
|
|
|
|
|
import aiohttp.client_exceptions |
|
|
|
import aiohttp.client_exceptions |
|
|
|
from aiohttp import ClientResponse |
|
|
|
from aiohttp import ClientResponse |
|
|
|
@ -44,6 +45,15 @@ def decode_lnurl(lnurl: str) -> str: |
|
|
|
return url |
|
|
|
return url |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _is_url_safe_enough_for_lnurl(url: str) -> bool: |
|
|
|
|
|
|
|
u = urllib.parse.urlparse(url) |
|
|
|
|
|
|
|
if u.scheme.lower() == "https": |
|
|
|
|
|
|
|
return True |
|
|
|
|
|
|
|
if u.netloc.endswith(".onion"): |
|
|
|
|
|
|
|
return True |
|
|
|
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LNURL6Data(NamedTuple): |
|
|
|
class LNURL6Data(NamedTuple): |
|
|
|
callback_url: str |
|
|
|
callback_url: str |
|
|
|
max_sendable_sat: int |
|
|
|
max_sendable_sat: int |
|
|
|
@ -55,6 +65,8 @@ class LNURL6Data(NamedTuple): |
|
|
|
|
|
|
|
|
|
|
|
async def _request_lnurl(url: str) -> dict: |
|
|
|
async def _request_lnurl(url: str) -> dict: |
|
|
|
"""Requests payment data from a lnurl.""" |
|
|
|
"""Requests payment data from a lnurl.""" |
|
|
|
|
|
|
|
if not _is_url_safe_enough_for_lnurl(url): |
|
|
|
|
|
|
|
raise LNURLError(f"This lnurl looks unsafe. It must use 'https://' or '.onion' (found: {url[:10]}...)") |
|
|
|
try: |
|
|
|
try: |
|
|
|
response_raw = await Network.async_send_http_on_proxy("get", url, timeout=10) |
|
|
|
response_raw = await Network.async_send_http_on_proxy("get", url, timeout=10) |
|
|
|
except asyncio.TimeoutError as e: |
|
|
|
except asyncio.TimeoutError as e: |
|
|
|
@ -92,6 +104,8 @@ async def request_lnurl(url: str) -> LNURL6Data: |
|
|
|
callback_url = lnurl_dict['callback'] |
|
|
|
callback_url = lnurl_dict['callback'] |
|
|
|
except KeyError as e: |
|
|
|
except KeyError as e: |
|
|
|
raise LNURLError(f"Missing 'callback' field in lnurl6 response.") from e |
|
|
|
raise LNURLError(f"Missing 'callback' field in lnurl6 response.") from e |
|
|
|
|
|
|
|
if not _is_url_safe_enough_for_lnurl(callback_url): |
|
|
|
|
|
|
|
raise LNURLError(f"This lnurl callback_url looks unsafe. It must use 'https://' or '.onion' (found: {callback_url[:10]}...)") |
|
|
|
# parse lnurl6 "minSendable"/"maxSendable" |
|
|
|
# parse lnurl6 "minSendable"/"maxSendable" |
|
|
|
try: |
|
|
|
try: |
|
|
|
max_sendable_sat = int(lnurl_dict['maxSendable']) // 1000 |
|
|
|
max_sendable_sat = int(lnurl_dict['maxSendable']) // 1000 |
|
|
|
@ -115,6 +129,8 @@ async def request_lnurl(url: str) -> LNURL6Data: |
|
|
|
|
|
|
|
|
|
|
|
async def callback_lnurl(url: str, params: dict) -> dict: |
|
|
|
async def callback_lnurl(url: str, params: dict) -> dict: |
|
|
|
"""Requests an invoice from a lnurl supporting server.""" |
|
|
|
"""Requests an invoice from a lnurl supporting server.""" |
|
|
|
|
|
|
|
if not _is_url_safe_enough_for_lnurl(url): |
|
|
|
|
|
|
|
raise LNURLError(f"This lnurl looks unsafe. It must use 'https://' or '.onion' (found: {url[:10]}...)") |
|
|
|
try: |
|
|
|
try: |
|
|
|
response_raw = await Network.async_send_http_on_proxy("get", url, params=params) |
|
|
|
response_raw = await Network.async_send_http_on_proxy("get", url, params=params) |
|
|
|
except asyncio.TimeoutError as e: |
|
|
|
except asyncio.TimeoutError as e: |
|
|
|
|