NAME

Net::Nostr::Calendar - NIP-52 Calendar Events

SYNOPSIS

use Net::Nostr::Calendar;

# Date-based calendar event (kind 31922)
my $event = Net::Nostr::Calendar->date_event(
    pubkey       => $hex_pubkey,
    identifier   => 'vacation-2024',
    title        => 'Summer Vacation',
    content      => 'Two weeks off',
    start        => '2024-07-01',
    end          => '2024-07-15',
    locations    => ['Beach Resort'],
    participants => [[$friend_pk, 'wss://relay', 'attendee']],
);

# Time-based calendar event (kind 31923)
my $event = Net::Nostr::Calendar->time_event(
    pubkey     => $hex_pubkey,
    identifier => 'standup',
    title      => 'Daily Standup',
    start      => 1700000000,
    end        => 1700003600,
    start_tzid => 'America/New_York',
    days       => [19675],
);

# Calendar (kind 31924)
my $cal = Net::Nostr::Calendar->calendar(
    pubkey     => $hex_pubkey,
    identifier => 'personal',
    title      => 'Personal Calendar',
    events     => [
        ["31922:$pk:vacation-2024", 'wss://relay'],
    ],
);

# RSVP (kind 31925)
my $rsvp = Net::Nostr::Calendar->rsvp(
    pubkey       => $hex_pubkey,
    identifier   => 'rsvp-1',
    event_coord  => "31922:$organizer_pk:vacation-2024",
    status       => 'accepted',
    fb           => 'busy',
);

# Parse any calendar event
my $parsed = Net::Nostr::Calendar->from_event($event);

# Validate
Net::Nostr::Calendar->validate($event);

DESCRIPTION

Implements NIP-52 (Calendar Events). Four addressable event kinds are used:

  • Date-Based Calendar Event (kind 31922) - All-day or multi-day events where time and time zone hold no significance.

  • Time-Based Calendar Event (kind 31923) - Events spanning between a start time and end time.

  • Calendar (kind 31924) - A collection of calendar events.

  • Calendar Event RSVP (kind 31925) - Attendance response to a calendar event.

All four kinds are addressable and deletable per NIP-09.

Common tags for calendar events

Both date-based and time-based calendar events share these tags:

  • d (required) - unique identifier

  • title (required) - title of the event

  • summary (optional) - brief description

  • image (optional) - URL of an image

  • location (optional, repeated) - location string

  • g (optional) - geohash for searchable location

  • p (optional, repeated) - participant pubkey, relay URL, and role

  • t (optional, repeated) - hashtag

  • r (optional, repeated) - reference URL

  • a (repeated) - reference to kind 31924 calendar

The deprecated name tag is mapped to title when parsing if title is not present.

CONSTRUCTOR

new

my $cal = Net::Nostr::Calendar->new(
    identifier => 'meeting',
    title      => 'Team Meeting',
);

Creates a new Net::Nostr::Calendar object. Croaks on unknown arguments. Array fields default to []. description defaults to ''.

CLASS METHODS

date_event

my $event = Net::Nostr::Calendar->date_event(
    pubkey       => $hex_pubkey,          # required
    identifier   => $id,                  # required (d tag)
    title        => $title,               # required (title tag)
    start        => 'YYYY-MM-DD',         # required
    end          => 'YYYY-MM-DD',         # optional
    content      => $description,         # optional, defaults to ''
    summary      => $text,                # optional
    image        => $url,                 # optional
    locations    => [$location],           # optional
    geohash      => $geohash,             # optional
    participants => [[$pk, $relay, $role]], # optional
    hashtags     => [$tag],               # optional
    references   => [$url],               # optional
    calendars    => [$coord],             # optional (a tags)
    created_at   => time(),               # optional
);

Creates a kind 31922 date-based calendar Net::Nostr::Event. The start date is inclusive and in ISO 8601 format (YYYY-MM-DD). The end date is exclusive. If end is omitted, the event ends on the same date as start. start must be less than end if both are present (enforced by "validate").

time_event

my $event = Net::Nostr::Calendar->time_event(
    pubkey       => $hex_pubkey,          # required
    identifier   => $id,                  # required (d tag)
    title        => $title,               # required (title tag)
    start        => $unix_timestamp,      # required
    end          => $unix_timestamp,      # optional
    start_tzid   => 'America/New_York',   # optional (IANA TZ)
    end_tzid     => 'America/New_York',   # optional (IANA TZ)
    days         => [$day_number],        # required per spec (D tags)
    content      => $description,         # optional, defaults to ''
    summary      => $text,                # optional
    image        => $url,                 # optional
    locations    => [$location],           # optional
    geohash      => $geohash,             # optional
    participants => [[$pk, $relay, $role]], # optional
    hashtags     => [$tag],               # optional
    references   => [$url],               # optional
    calendars    => [$coord],             # optional (a tags)
    created_at   => time(),               # optional
);

Creates a kind 31923 time-based calendar Net::Nostr::Event. The start timestamp is inclusive (Unix seconds). The end timestamp is exclusive. If end is omitted, the event ends instantaneously. start must be less than end if both are present (enforced by "validate"). days are day-granularity timestamps (D tags) calculated as floor(unix_seconds / 86400). The spec requires at least one D tag (enforced by "validate").

calendar

my $event = Net::Nostr::Calendar->calendar(
    pubkey     => $hex_pubkey,            # required
    identifier => $id,                    # required (d tag)
    title      => $title,                 # required (title tag)
    content    => $description,           # optional, defaults to ''
    events     => [[$coord, $relay]],     # optional (a tags)
);

Creates a kind 31924 calendar Net::Nostr::Event. events is an arrayref of arrayrefs, each containing a calendar event coordinate and optional relay URL.

rsvp

my $event = Net::Nostr::Calendar->rsvp(
    pubkey             => $hex_pubkey,    # required
    identifier         => $id,            # required (d tag)
    event_coord        => $coord,         # required (a tag)
    status             => 'accepted',     # required
    event_relay        => $url,           # optional (a tag relay)
    event_id           => $eid,           # optional (e tag)
    event_id_relay     => $url,           # optional (e tag relay)
    fb                 => 'busy',         # optional (free/busy)
    event_author       => $pk,            # optional (p tag)
    event_author_relay => $url,           # optional (p tag relay)
    content            => $note,          # optional, defaults to ''
);

Creates a kind 31925 RSVP Net::Nostr::Event. status must be accepted, declined, or tentative. The fb tag is automatically omitted when status is declined.

from_event

my $cal = Net::Nostr::Calendar->from_event($event);

Parses a kind 31922, 31923, 31924, or 31925 event into a Net::Nostr::Calendar object. Returns undef for unrecognized kinds. Handles the deprecated name tag (mapped to title if title is absent).

validate

Net::Nostr::Calendar->validate($event);

Validates a NIP-52 event. Croaks if:

  • Kind is not 31922, 31923, 31924, or 31925

  • Kind 31922/31923 missing d, title, or start tag

  • Kind 31922/31923 start is not less than end (when end is present)

  • Kind 31923 missing D tag

  • Kind 31924 missing d or title tag

  • Kind 31925 missing d, a, or status tag

  • Kind 31925 status is not accepted, declined, or tentative

Returns 1 on success.

ACCESSORS

identifier

The d tag value.

title

The calendar event or calendar title.

description

The event content. Defaults to ''.

start

The start date (YYYY-MM-DD for kind 31922) or Unix timestamp string (for kind 31923).

end

The end date or Unix timestamp string, or undef.

summary

Brief description, or undef.

image

Image URL, or undef.

locations

Arrayref of location strings. Defaults to [].

geohash

Geohash string, or undef.

participants

Arrayref of arrayrefs, each containing pubkey, optional relay URL, and optional role. Defaults to [].

hashtags

Arrayref of hashtag strings. Defaults to [].

references

Arrayref of reference URL strings. Defaults to [].

calendars

Arrayref of kind 31924 calendar coordinates (from a tags in calendar events). Defaults to [].

calendar_events

Arrayref of arrayrefs, each containing a calendar event coordinate and optional relay URL (from a tags in calendars). Defaults to [].

start_tzid

IANA Time Zone Database identifier for the start time (kind 31923 only).

end_tzid

IANA Time Zone Database identifier for the end time (kind 31923 only).

days

Arrayref of day-granularity timestamp strings (D tags, kind 31923 only). Defaults to [].

event_coord

The calendar event coordinate (a tag value, RSVP only).

event_id

The specific calendar event revision ID (e tag value, RSVP only).

status

RSVP status: accepted, declined, or tentative.

fb

Free/busy indicator: free or busy. Must be omitted or ignored if status is declined.

event_author

Pubkey of the calendar event author (RSVP only).

SEE ALSO

NIP-52, Net::Nostr, Net::Nostr::Event