diff --git a/config.json b/config.json index c8adf29..df70ae3 100644 --- a/config.json +++ b/config.json @@ -1,6 +1,6 @@ { "id": "events", - "version": "1.3.0-aio.3", + "version": "1.3.0-aio.2", "name": "Events", "repo": "https://git.atitlan.io/aiolabs/events", "short_description": "Sell and register event tickets", diff --git a/models.py b/models.py index a617a13..56b5f42 100644 --- a/models.py +++ b/models.py @@ -30,11 +30,9 @@ class CreateEvent(BaseModel): wallet: str | None = None # filled from caller's wallet if absent name: str # title (required) info: str = "" # description (optional) - closing_date: str | None = None # date-only YYYY-MM-DD; defaults to event_end_date - # ISO 8601: date-only ("2026-05-19") or datetime ("2026-05-19T18:30"). - # Presence of a "T" toggles NIP-52 kind (31922 date / 31923 time). - event_start_date: str - event_end_date: str | None = None # same format as event_start_date + closing_date: str | None = None # defaults to event_end_date + event_start_date: str # required + event_end_date: str | None = None # defaults to event_start_date currency: str = "sat" amount_tickets: int = 0 # 0 = unlimited / not ticketed price_per_ticket: float = 0 # 0 = free diff --git a/nostr_publisher.py b/nostr_publisher.py index a6d487b..d9f46c9 100644 --- a/nostr_publisher.py +++ b/nostr_publisher.py @@ -1,17 +1,14 @@ """ NIP-52 calendar event publishing for the events extension. -Builds NIP-52 calendar events from the Event model, signs them with the -creator's Account keypair, and publishes via the NostrClient. - -Kind 31922 is used for date-only events; kind 31923 (time-based) is used -when event_start_date / event_end_date include a time component. +Builds kind 31922 (date-based) calendar events from the Event model, +signs them with the event creator's Account keypair, and publishes +via the NostrClient to nostrclient relays. Reference: https://github.com/nostr-protocol/nips/blob/master/52.md """ import time -from datetime import datetime, timezone import coincurve from loguru import logger @@ -20,60 +17,26 @@ from .models import Event from .nostr.event import NostrEvent -def _has_time(value: str | None) -> bool: - """ISO 8601 datetime strings contain a 'T' between date and time.""" - return value is not None and "T" in value - - -def _to_unix(value: str) -> int: - """Parse ISO 8601 datetime (assume UTC if naive) to unix seconds.""" - dt = datetime.fromisoformat(value) - if dt.tzinfo is None: - dt = dt.replace(tzinfo=timezone.utc) - return int(dt.timestamp()) - - def build_nip52_event(event: Event, pubkey: str) -> NostrEvent: """ - Convert an Event model to a NIP-52 calendar event. + Convert an Event model to a NIP-52 kind 31922 (date-based) calendar event. - Time-based (kind 31923) if event_start_date carries an HH:MM, otherwise - date-based (kind 31922). Tags: - d - event.id + Tags: + d - event.id (addressable identifier) title - event.name - start - unix timestamp (31923) or YYYY-MM-DD (31922) - end - same encoding (optional) - image, location, t (categories) - optional - Content: event.info + start - event.event_start_date (ISO date string) + end - event.event_end_date (optional) + image - event.banner (optional) + Content: event.info (description) """ - time_based = _has_time(event.event_start_date) - kind = 31923 if time_based else 31922 - start_value = ( - str(_to_unix(event.event_start_date)) if time_based else event.event_start_date - ) - tags = [ ["d", event.id], ["title", event.name], - ["start", start_value], + ["start", event.event_start_date], ] - end_unix: int | None = None if event.event_end_date: - end_value = ( - str(_to_unix(event.event_end_date)) if time_based else event.event_end_date - ) - tags.append(["end", end_value]) - if time_based: - end_unix = _to_unix(event.event_end_date) - - if time_based: - start_unix = _to_unix(event.event_start_date) - start_day = start_unix // 86400 - end_day = (end_unix // 86400) if end_unix is not None else start_day - for day in range(start_day, end_day + 1): - tags.append(["D", str(day)]) - + tags.append(["end", event.event_end_date]) if event.banner: tags.append(["image", event.banner]) if event.location: @@ -84,7 +47,7 @@ def build_nip52_event(event: Event, pubkey: str) -> NostrEvent: nostr_event = NostrEvent( pubkey=pubkey, created_at=int(time.time()), - kind=kind, + kind=31922, tags=tags, content=event.info or "", ) @@ -96,17 +59,15 @@ def build_nip52_delete_event(event: Event, pubkey: str) -> NostrEvent: """ Build a kind 5 delete event for a published NIP-52 calendar event. - Uses an 'a' tag to reference the parameterized replaceable event per - NIP-09. The referenced kind must match what we published — 31923 for - time-based events, 31922 for date-only. + Uses an 'a' tag to reference the parameterized replaceable event + (kind 31922) per NIP-09. """ - referenced_kind = 31923 if _has_time(event.event_start_date) else 31922 nostr_event = NostrEvent( pubkey=pubkey, created_at=int(time.time()), kind=5, tags=[ - ["a", f"{referenced_kind}:{pubkey}:{event.id}"], + ["a", f"31922:{pubkey}:{event.id}"], ], content="Event canceled", ) diff --git a/static/js/index.js b/static/js/index.js index 5ff1c42..22a73c0 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -233,39 +233,11 @@ window.PageEvents = { .catch(LNbits.utils.notifyApiError) }) }, - foldDateTime(day, time) { - // Combine separate date/time inputs into the wire format - // expected by the events extension: "YYYY-MM-DD" or - // "YYYY-MM-DDTHH:MM" (time is optional). - if (!day) return null - return time ? `${day}T${time}` : day - }, - splitDateTime(value) { - // Inverse of foldDateTime: split a stored string back into the - // day/time pieces the form inputs bind to. - if (!value) return {day: '', time: ''} - const [day, time = ''] = value.split('T') - // Time inputs only accept HH:MM, drop any seconds we stored. - return {day, time: time.slice(0, 5)} - }, sendEventData() { const wallet = _.findWhere(this.g.user.wallets, { id: this.formDialog.data.wallet }) - const data = {...this.formDialog.data} - data.event_start_date = this.foldDateTime( - data.event_start_day, - data.event_start_time - ) - data.event_end_date = this.foldDateTime( - data.event_end_day, - data.event_end_time - ) - delete data.event_start_day - delete data.event_start_time - delete data.event_end_day - delete data.event_end_time - + const data = this.formDialog.data if (data.extra && !data.extra.promo_codes) { data.extra.promo_codes = data.extra.promo_codes .filter(code => code.trim() !== '') @@ -281,22 +253,10 @@ window.PageEvents = { openEventDialog(data = false) { if (data && data.id) { - const start = this.splitDateTime(data.event_start_date) - const end = this.splitDateTime(data.event_end_date) - this.formDialog.data = { - ...data, - event_start_day: start.day, - event_start_time: start.time, - event_end_day: end.day, - event_end_time: end.time - } + this.formDialog.data = {...data} } else { this.formDialog.data = { currency: 'sats', - event_start_day: '', - event_start_time: '', - event_end_day: '', - event_end_time: '', extra: { conditional: false, min_tickets: 1, diff --git a/static/js/index.vue b/static/js/index.vue index df3a990..5aa1579 100644 --- a/static/js/index.vue +++ b/static/js/index.vue @@ -471,46 +471,28 @@ > -