Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,9 @@ jobs:
tests:
runs-on: ubuntu-latest
needs: [lint]
strategy:
matrix:
python-version: ['3.9', '3.10']
steps:
- uses: actions/checkout@v4
- uses: lnbits/lnbits/.github/actions/prepare@dev
with:
python-version: ${{ matrix.python-version }}
- name: Run pytest
uses: pavelzw/pytest-action@v2
env:
Expand All @@ -30,5 +25,5 @@ jobs:
job-summary: true
emoji: false
click-to-expand: true
custom-pytest: poetry run pytest
report-title: 'test (${{ matrix.python-version }})'
custom-pytest: uv run pytest
report-title: 'test'
24 changes: 12 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,27 @@ format: prettier black ruff
check: mypy pyright checkblack checkruff checkprettier

prettier:
poetry run ./node_modules/.bin/prettier --write .
uv run ./node_modules/.bin/prettier --write .
pyright:
poetry run ./node_modules/.bin/pyright
uv run ./node_modules/.bin/pyright

mypy:
poetry run mypy .
uv run mypy .

black:
poetry run black .
uv run black .

ruff:
poetry run ruff check . --fix
uv run ruff check . --fix

checkruff:
poetry run ruff check .
uv run ruff check .

checkprettier:
poetry run ./node_modules/.bin/prettier --check .
uv run ./node_modules/.bin/prettier --check .

checkblack:
poetry run black --check .
uv run black --check .

checkeditorconfig:
editorconfig-checker
Expand All @@ -34,15 +34,15 @@ test:
LNBITS_DATA_FOLDER="./tests/data" \
PYTHONUNBUFFERED=1 \
DEBUG=true \
poetry run pytest
uv run pytest

install-pre-commit-hook:
@echo "Installing pre-commit hook to git"
@echo "Uninstall the hook with poetry run pre-commit uninstall"
poetry run pre-commit install
@echo "Uninstall the hook with uv run pre-commit uninstall"
uv run pre-commit install

pre-commit:
poetry run pre-commit run --all-files
uv run pre-commit run --all-files


checkbundle:
Expand Down
13 changes: 6 additions & 7 deletions crud.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import json
from typing import Optional

from lnbits.db import Database

Expand All @@ -21,15 +20,15 @@ async def update_relay(relay: NostrRelay) -> NostrRelay:
return relay


async def get_relay(user_id: str, relay_id: str) -> Optional[NostrRelay]:
async def get_relay(user_id: str, relay_id: str) -> NostrRelay | None:
return await db.fetchone(
"SELECT * FROM nostrrelay.relays WHERE user_id = :user_id AND id = :id",
{"user_id": user_id, "id": relay_id},
NostrRelay,
)


async def get_relay_by_id(relay_id: str) -> Optional[NostrRelay]:
async def get_relay_by_id(relay_id: str) -> NostrRelay | None:
"""Note: it does not require `user_id`. Can read any relay. Use it with care."""
return await db.fetchone(
"SELECT * FROM nostrrelay.relays WHERE id = :id",
Expand Down Expand Up @@ -58,7 +57,7 @@ async def get_config_for_all_active_relays() -> dict:
return active_relay_configs


async def get_public_relay(relay_id: str) -> Optional[dict]:
async def get_public_relay(relay_id: str) -> dict | None:
relay = await db.fetchone(
"SELECT * FROM nostrrelay.relays WHERE id = :id",
{"id": relay_id},
Expand Down Expand Up @@ -130,7 +129,7 @@ async def get_events(
return events


async def get_event(relay_id: str, event_id: str) -> Optional[NostrEvent]:
async def get_event(relay_id: str, event_id: str) -> NostrEvent | None:
event = await db.fetchone(
"SELECT * FROM nostrrelay.events WHERE relay_id = :relay_id AND id = :id",
{"relay_id": relay_id, "id": event_id},
Expand Down Expand Up @@ -206,7 +205,7 @@ async def delete_events(relay_id: str, nostr_filter: NostrFilter):
else:
# Simple DELETE without JOINs
query = f"DELETE FROM events WHERE {' AND '.join(where)}"

await db.execute(query, values)
# todo: delete tags

Expand Down Expand Up @@ -286,7 +285,7 @@ async def delete_account(relay_id: str, pubkey: str):
async def get_account(
relay_id: str,
pubkey: str,
) -> Optional[NostrAccount]:
) -> NostrAccount | None:
return await db.fetchone(
"""
SELECT * FROM nostrrelay.accounts
Expand Down
8 changes: 3 additions & 5 deletions models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from typing import Optional

from pydantic import BaseModel


Expand All @@ -16,8 +14,8 @@ def is_valid_action(self) -> bool:
class NostrPartialAccount(BaseModel):
relay_id: str
pubkey: str
allowed: Optional[bool] = None
blocked: Optional[bool] = None
allowed: bool | None = None
blocked: bool | None = None


class NostrAccount(BaseModel):
Expand All @@ -44,4 +42,4 @@ class NostrEventTags(BaseModel):
event_id: str
name: str
value: str
extra: Optional[str] = None
extra: str | None = None
2,629 changes: 0 additions & 2,629 deletions poetry.lock

This file was deleted.

60 changes: 30 additions & 30 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
[tool.poetry]
[project]
name = "nostrrelay"
version = "0.0.0"
description = "nostrrelay"
authors = ["dni <dni@lnbits.com>"]

[tool.poetry.dependencies]
python = "^3.10 | ^3.9"
lnbits = {allow-prereleases = true, version = "*"}

[tool.poetry.group.dev.dependencies]
black = "^24.3.0"
pytest-asyncio = "^0.21.0"
pytest = "^7.3.2"
mypy = "^1.5.1"
pre-commit = "^3.2.2"
ruff = "^0.3.2"
pytest-md = "^0.2.0"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
requires-python = ">=3.10,<3.13"
description = "LNbits, free and open-source Lightning wallet and accounts system."
authors = [{ name = "Alan Bits", email = "alan@lnbits.com" }]
urls = { Homepage = "https://lnbits.com", Repository = "https://github.com/lnbits/nostrrelay" }
dependencies = [ "lnbits>1" ]

[tool.mypy]
exclude = [
"boltz_client"
[tool.poetry]
package-mode = false

[tool.uv]
dev-dependencies = [
"black",
"pytest-asyncio",
"pytest",
"mypy",
"pre-commit",
"ruff",
"pytest-md",
]

[tool.mypy]
plugins = ["pydantic.mypy"]

[[tool.mypy.overrides]]
module = [
"lnbits.*",
"loguru.*",
"fastapi.*",
"pydantic.*",
"embit.*",
"secp256k1.*",
]
ignore_missing_imports = "True"

[tool.pydantic-mypy]
init_forbid_extra = true
init_typed = true
warn_required_dynamic_aliases = true
warn_untyped_fields = true

[tool.pytest.ini_options]
log_cli = false
testpaths = [
Expand Down Expand Up @@ -86,8 +86,8 @@ classmethod-decorators = [
# [tool.ruff.lint.extend-per-file-ignores]
# "views_api.py" = ["F401"]

# [tool.ruff.lint.mccabe]
# max-complexity = 10
[tool.ruff.lint.mccabe]
max-complexity = 11

[tool.ruff.lint.flake8-bugbear]
# Allow default arguments like, e.g., `data: List[str] = fastapi.Query(None)`.
Expand Down
39 changes: 21 additions & 18 deletions relay/client_connection.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
import time
from typing import Any, Awaitable, Callable, List, Optional
from collections.abc import Awaitable, Callable
from typing import Any

from fastapi import WebSocket
from lnbits.helpers import urlsafe_short_hash
Expand All @@ -25,17 +26,17 @@ class NostrClientConnection:
def __init__(self, relay_id: str, websocket: WebSocket):
self.websocket = websocket
self.relay_id = relay_id
self.filters: List[NostrFilter] = []
self.auth_pubkey: Optional[str] = None # set if authenticated
self._auth_challenge: Optional[str] = None
self.filters: list[NostrFilter] = []
self.auth_pubkey: str | None = None # set if authenticated
self._auth_challenge: str | None = None
self._auth_challenge_created_at = 0

self.event_validator = EventValidator(self.relay_id)

self.broadcast_event: Optional[
Callable[[NostrClientConnection, NostrEvent], Awaitable[None]]
] = None
self.get_client_config: Optional[Callable[[], RelaySpec]] = None
self.broadcast_event: (
Callable[[NostrClientConnection, NostrEvent], Awaitable[None]] | None
) = None
self.get_client_config: Callable[[], RelaySpec] | None = None

async def start(self):
await self.websocket.accept()
Expand All @@ -50,7 +51,7 @@ async def start(self):
except Exception as e:
logger.warning(e)

async def stop(self, reason: Optional[str]):
async def stop(self, reason: str | None):
message = reason if reason else "Server closed webocket"
try:
await self._send_msg(["NOTICE", message])
Expand Down Expand Up @@ -98,7 +99,7 @@ async def _broadcast_event(self, e: NostrEvent):
if self.broadcast_event:
await self.broadcast_event(self, e)

async def _handle_message(self, data: List) -> List:
async def _handle_message(self, data: list) -> list:
if len(data) < 2:
return []

Expand All @@ -121,7 +122,9 @@ async def _handle_message(self, data: List) -> List:
# Handle multiple filters in REQ message
responses = []
for filter_data in data[2:]:
response = await self._handle_request(subscription_id, NostrFilter.parse_obj(filter_data))
response = await self._handle_request(
subscription_id, NostrFilter.parse_obj(filter_data)
)
responses.extend(response)
return responses
if message_type == NostrEventType.CLOSE:
Expand All @@ -133,7 +136,7 @@ async def _handle_message(self, data: List) -> List:

async def _handle_event(self, e: NostrEvent):
logger.info(f"nostr event: [{e.kind}, {e.pubkey}, '{e.content}']")
resp_nip20: List[Any] = ["OK", e.id]
resp_nip20: list[Any] = ["OK", e.id]

if e.is_auth_response_event:
valid, message = self.event_validator.validate_auth_event(
Expand Down Expand Up @@ -172,12 +175,12 @@ async def _handle_event(self, e: NostrEvent):

if d_tag_value:
deletion_filter = NostrFilter(
kinds=[e.kind],
kinds=[e.kind],
authors=[e.pubkey],
**{"#d": [d_tag_value]},
until=e.created_at
**{"#d": [d_tag_value]}, # type: ignore
until=e.created_at,
)

await delete_events(self.relay_id, deletion_filter)
if not e.is_ephemeral_event:
await create_event(e)
Expand All @@ -201,7 +204,7 @@ def config(self) -> RelaySpec:
raise Exception("Client not ready!")
return self.get_client_config()

async def _send_msg(self, data: List):
async def _send_msg(self, data: list):
await self.websocket.send_text(json.dumps(data))

async def _handle_delete_event(self, event: NostrEvent):
Expand All @@ -214,7 +217,7 @@ async def _handle_delete_event(self, event: NostrEvent):

async def _handle_request(
self, subscription_id: str, nostr_filter: NostrFilter
) -> List:
) -> list:
if self.config.require_auth_filter:
if not self.auth_pubkey:
return [["AUTH", self._current_auth_challenge()]]
Expand Down
4 changes: 1 addition & 3 deletions relay/client_manager.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from typing import List

from ..crud import get_config_for_all_active_relays
from .client_connection import NostrClientConnection
from .event import NostrEvent
Expand Down Expand Up @@ -47,7 +45,7 @@ async def disable_relay(self, relay_id: str):
def get_relay_config(self, relay_id: str) -> RelaySpec:
return self._active_relays[relay_id]

def clients(self, relay_id: str) -> List[NostrClientConnection]:
def clients(self, relay_id: str) -> list[NostrClientConnection]:
if relay_id not in self._clients:
self._clients[relay_id] = []
return self._clients[relay_id]
Expand Down
Loading
Loading