chore: satisfy upstream lint (black, mypy, prettier, ruff)
Some checks failed
lint.yml / chore: satisfy upstream lint (black, mypy, prettier, ruff) (push) Failing after 0s
Some checks failed
lint.yml / chore: satisfy upstream lint (black, mypy, prettier, ruff) (push) Failing after 0s
- black/prettier reformatting across new aio code - type annotations on db.fetchone/fetchall callsites in crud.py - explicit dict[str, list[str]] for tag_lists in nostr_sync.py - type:ignore[attr-defined] on Account.prvkey access — the field is added by the aio-fork lnbits.core.models.Account; upstream lnbits does not yet have it, so consumers without the fork must add a prvkey column to accounts before the Nostr publisher can sign. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
42a373bff1
commit
b428b0dca8
11 changed files with 48 additions and 57 deletions
|
|
@ -75,9 +75,7 @@ def events_start():
|
|||
except Exception as exc:
|
||||
logger.error(f"[EVENTS] Nostr sync task failed: {exc}")
|
||||
|
||||
task3 = create_permanent_unique_task(
|
||||
"ext_events_nostr_sync", _sync_nostr_events
|
||||
)
|
||||
task3 = create_permanent_unique_task("ext_events_nostr_sync", _sync_nostr_events)
|
||||
scheduled_tasks.append(task3)
|
||||
|
||||
|
||||
|
|
|
|||
12
crud.py
12
crud.py
|
|
@ -94,7 +94,7 @@ async def update_ticket(ticket: Ticket) -> Ticket:
|
|||
|
||||
|
||||
async def get_ticket(payment_hash: str) -> Ticket | None:
|
||||
row = await db.fetchone(
|
||||
row: dict | None = await db.fetchone(
|
||||
"SELECT * FROM events.ticket WHERE id = :id",
|
||||
{"id": payment_hash},
|
||||
)
|
||||
|
|
@ -107,13 +107,15 @@ async def get_tickets(wallet_ids: str | list[str]) -> list[Ticket]:
|
|||
if isinstance(wallet_ids, str):
|
||||
wallet_ids = [wallet_ids]
|
||||
q = ",".join([f"'{wallet_id}'" for wallet_id in wallet_ids])
|
||||
rows = await db.fetchall(f"SELECT * FROM events.ticket WHERE wallet IN ({q})")
|
||||
rows: list[dict] = await db.fetchall(
|
||||
f"SELECT * FROM events.ticket WHERE wallet IN ({q})"
|
||||
)
|
||||
return [Ticket(**_parse_ticket_row(row)) for row in rows]
|
||||
|
||||
|
||||
async def get_tickets_by_user_id(user_id: str) -> list[Ticket]:
|
||||
"""All tickets owned by the given LNbits user_id."""
|
||||
rows = await db.fetchall(
|
||||
rows: list[dict] = await db.fetchall(
|
||||
"SELECT * FROM events.ticket WHERE user_id = :user_id ORDER BY time DESC",
|
||||
{"user_id": user_id},
|
||||
)
|
||||
|
|
@ -206,7 +208,7 @@ async def get_pending_events() -> list[Event]:
|
|||
|
||||
async def get_settings() -> EventsSettings:
|
||||
"""Singleton settings row, seeded by m010."""
|
||||
row = await db.fetchone("SELECT * FROM events.settings WHERE id = 1")
|
||||
row: dict | None = await db.fetchone("SELECT * FROM events.settings WHERE id = 1")
|
||||
if row:
|
||||
return EventsSettings(**dict(row))
|
||||
return EventsSettings()
|
||||
|
|
@ -225,7 +227,7 @@ async def delete_event(event_id: str) -> None:
|
|||
|
||||
|
||||
async def get_event_tickets(event_id: str) -> list[Ticket]:
|
||||
rows = await db.fetchall(
|
||||
rows: list[dict] = await db.fetchall(
|
||||
"SELECT * FROM events.ticket WHERE event = :event",
|
||||
{"event": event_id},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -232,14 +232,12 @@ async def m010_add_events_settings(db):
|
|||
Create the extension settings singleton row used by the admin UI to
|
||||
toggle e.g. auto_approve.
|
||||
"""
|
||||
await db.execute(
|
||||
"""
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS events.settings (
|
||||
id INTEGER PRIMARY KEY DEFAULT 1,
|
||||
auto_approve BOOLEAN NOT NULL DEFAULT FALSE
|
||||
)
|
||||
"""
|
||||
)
|
||||
""")
|
||||
await db.execute(
|
||||
"INSERT INTO events.settings (id, auto_approve) "
|
||||
"SELECT 1, FALSE WHERE NOT EXISTS "
|
||||
|
|
|
|||
|
|
@ -118,9 +118,7 @@ class CreateTicket(BaseModel):
|
|||
email = values.get("email")
|
||||
user_id = values.get("user_id")
|
||||
if not user_id and not (name and email):
|
||||
raise ValueError(
|
||||
"Either user_id or both name and email must be provided"
|
||||
)
|
||||
raise ValueError("Either user_id or both name and email must be provided")
|
||||
if user_id and (name or email):
|
||||
raise ValueError("Cannot provide both user_id and name/email")
|
||||
return values
|
||||
|
|
|
|||
|
|
@ -39,8 +39,7 @@ class NostrClient:
|
|||
async def connect(self) -> WebSocketApp:
|
||||
relay_endpoint = encrypt_internal_message("relay", urlsafe=True)
|
||||
ws_url = (
|
||||
f"ws://localhost:{settings.port}"
|
||||
f"/nostrclient/api/v1/{relay_endpoint}"
|
||||
f"ws://localhost:{settings.port}" f"/nostrclient/api/v1/{relay_endpoint}"
|
||||
)
|
||||
|
||||
logger.info("[EVENTS] Connecting to nostrclient WebSocket...")
|
||||
|
|
@ -58,12 +57,8 @@ class NostrClient:
|
|||
logger.warning(f"[EVENTS] WebSocket error: {error}")
|
||||
|
||||
def on_close(_, status_code, message):
|
||||
logger.warning(
|
||||
f"[EVENTS] WebSocket closed: {status_code} {message}"
|
||||
)
|
||||
self.receive_event_queue.put_nowait(
|
||||
ValueError("WebSocket closed")
|
||||
)
|
||||
logger.warning(f"[EVENTS] WebSocket closed: {status_code} {message}")
|
||||
self.receive_event_queue.put_nowait(ValueError("WebSocket closed"))
|
||||
|
||||
ws = WebSocketApp(
|
||||
ws_url,
|
||||
|
|
@ -118,9 +113,7 @@ class NostrClient:
|
|||
async def subscribe(self, filters: list[dict]):
|
||||
"""Subscribe to events matching the given filters."""
|
||||
self.subscription_id = "events-" + urlsafe_short_hash()[:32]
|
||||
await self.send_req_queue.put(
|
||||
["REQ", self.subscription_id, *filters]
|
||||
)
|
||||
await self.send_req_queue.put(["REQ", self.subscription_id, *filters])
|
||||
logger.info(
|
||||
f"[EVENTS] Subscribed to NIP-52 events "
|
||||
f"(sub: {self.subscription_id[:20]}...)"
|
||||
|
|
|
|||
|
|
@ -29,11 +29,15 @@ async def publish_or_delete_nostr_event(event: Event, *, delete: bool = False) -
|
|||
if not wallet_obj:
|
||||
return
|
||||
account = await get_account(wallet_obj.user)
|
||||
if not account or not account.pubkey or not account.prvkey:
|
||||
if not account or not account.pubkey or not account.prvkey: # type: ignore[attr-defined]
|
||||
return
|
||||
|
||||
nostr_event = await publish_event_to_nostr(
|
||||
nostr_client, event, account.pubkey, account.prvkey, delete=delete
|
||||
nostr_client,
|
||||
event,
|
||||
account.pubkey,
|
||||
account.prvkey, # type: ignore[attr-defined]
|
||||
delete=delete,
|
||||
)
|
||||
if nostr_event and not delete:
|
||||
event.nostr_event_id = nostr_event.id
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ def build_nip52_event(event: Event, pubkey: str) -> NostrEvent:
|
|||
tags.append(["image", event.banner])
|
||||
if event.location:
|
||||
tags.append(["location", event.location])
|
||||
for cat in (event.categories or []):
|
||||
for cat in event.categories or []:
|
||||
tags.append(["t", cat])
|
||||
|
||||
nostr_event = NostrEvent(
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ async def _handle_calendar_event(nostr_client: NostrClient, event_data: dict):
|
|||
return
|
||||
|
||||
tags = {t[0]: t[1] for t in event_data.get("tags", []) if len(t) >= 2}
|
||||
tag_lists = {}
|
||||
tag_lists: dict[str, list[str]] = {}
|
||||
for t in event_data.get("tags", []):
|
||||
if len(t) >= 2:
|
||||
tag_lists.setdefault(t[0], []).append(t[1])
|
||||
|
|
@ -137,9 +137,11 @@ async def wait_for_nostr_events(nostr_client: NostrClient):
|
|||
while True:
|
||||
try:
|
||||
# Subscribe to NIP-52 calendar events
|
||||
await nostr_client.subscribe([
|
||||
await nostr_client.subscribe(
|
||||
[
|
||||
{"kinds": [31922, 31923]},
|
||||
])
|
||||
]
|
||||
)
|
||||
|
||||
# Process incoming events
|
||||
while True:
|
||||
|
|
|
|||
|
|
@ -187,12 +187,7 @@ window.PageEvents = {
|
|||
},
|
||||
saveSettings() {
|
||||
LNbits.api
|
||||
.request(
|
||||
'PUT',
|
||||
'/events/api/v1/events/settings',
|
||||
null,
|
||||
this.settings
|
||||
)
|
||||
.request('PUT', '/events/api/v1/events/settings', null, this.settings)
|
||||
.then(() => {
|
||||
Quasar.Notify.create({type: 'positive', message: 'Settings saved'})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -192,7 +192,13 @@
|
|||
<q-td v-for="col in props.cols" :key="col.name" :props="props">
|
||||
<q-badge
|
||||
v-if="col.name === 'status'"
|
||||
:color="col.value === 'approved' ? 'green' : col.value === 'proposed' ? 'orange' : 'red'"
|
||||
:color="
|
||||
col.value === 'approved'
|
||||
? 'green'
|
||||
: col.value === 'proposed'
|
||||
? 'orange'
|
||||
: 'red'
|
||||
"
|
||||
:label="col.value"
|
||||
></q-badge>
|
||||
<span v-else v-text="col.value"></span>
|
||||
|
|
@ -284,7 +290,13 @@
|
|||
<q-td v-for="col in props.cols" :key="col.name" :props="props">
|
||||
<q-badge
|
||||
v-if="col.name === 'status'"
|
||||
:color="col.value === 'approved' ? 'green' : col.value === 'proposed' ? 'orange' : 'red'"
|
||||
:color="
|
||||
col.value === 'approved'
|
||||
? 'green'
|
||||
: col.value === 'proposed'
|
||||
? 'orange'
|
||||
: 'red'
|
||||
"
|
||||
:label="col.value"
|
||||
></q-badge>
|
||||
<span v-else v-text="col.value"></span>
|
||||
|
|
|
|||
21
views_api.py
21
views_api.py
|
|
@ -139,9 +139,7 @@ async def api_get_event(event_id: str) -> Event:
|
|||
|
||||
# closing_date is filled in by create_event (defaults to end_date or
|
||||
# start_date) but the field is typed Optional, so guard for the typechecker.
|
||||
closing_date = (
|
||||
event.closing_date or event.event_end_date or event.event_start_date
|
||||
)
|
||||
closing_date = event.closing_date or event.event_end_date or event.event_start_date
|
||||
is_window_open = datetime.now(timezone.utc) < datetime.strptime(
|
||||
closing_date, "%Y-%m-%d"
|
||||
).replace(tzinfo=timezone.utc)
|
||||
|
|
@ -181,10 +179,7 @@ async def api_event_create(
|
|||
|
||||
ext_settings = await get_settings()
|
||||
user_id = wallet.wallet.user
|
||||
is_admin = (
|
||||
user_id == settings.super_user
|
||||
or user_id in settings.lnbits_admin_users
|
||||
)
|
||||
is_admin = user_id == settings.super_user or user_id in settings.lnbits_admin_users
|
||||
if not is_admin and not ext_settings.auto_approve:
|
||||
data.status = "proposed"
|
||||
|
||||
|
|
@ -208,9 +203,7 @@ async def api_event_update(
|
|||
status_code=HTTPStatus.NOT_FOUND, detail="Event does not exist."
|
||||
)
|
||||
if event.wallet != wallet.wallet.id:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.FORBIDDEN, detail="Not your event."
|
||||
)
|
||||
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not your event.")
|
||||
for k, v in data.dict().items():
|
||||
setattr(event, k, v)
|
||||
event = await update_event(event)
|
||||
|
|
@ -351,9 +344,7 @@ async def api_get_ticket(ticket_id: str) -> Ticket:
|
|||
|
||||
|
||||
@tickets_api_router.post("/{event_id}")
|
||||
async def api_ticket_create(
|
||||
event_id: str, data: CreateTicket
|
||||
) -> TicketPaymentRequest:
|
||||
async def api_ticket_create(event_id: str, data: CreateTicket) -> TicketPaymentRequest:
|
||||
event = await get_event(event_id)
|
||||
if not event:
|
||||
raise HTTPException(
|
||||
|
|
@ -423,9 +414,7 @@ async def _create_named_ticket(
|
|||
)
|
||||
|
||||
|
||||
async def _create_user_id_ticket(
|
||||
event: Event, user_id: str
|
||||
) -> TicketPaymentRequest:
|
||||
async def _create_user_id_ticket(event: Event, user_id: str) -> TicketPaymentRequest:
|
||||
price = event.price_per_ticket
|
||||
extra: dict[str, Any] = {"tag": "events", "user_id": user_id}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue