Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 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