Net-Nostr

 view release on metacpan or  search on metacpan

lib/Net/Nostr/Calendar.pm  view on Meta::CPAN

        croak "RSVP MUST have a 'status' tag"  unless exists $has{status};
        croak "RSVP status MUST be accepted, declined, or tentative"
            unless $has{status} =~ /\A(?:accepted|declined|tentative)\z/;
    }

    return 1;
}

1;

__END__


=head1 NAME

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

=head1 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);

=head1 DESCRIPTION

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

=over 4

=item * B<Date-Based Calendar Event> (kind 31922) - All-day or multi-day
events where time and time zone hold no significance.

=item * B<Time-Based Calendar Event> (kind 31923) - Events spanning
between a start time and end time.

=item * B<Calendar> (kind 31924) - A collection of calendar events.

=item * B<Calendar Event RSVP> (kind 31925) - Attendance response to a
calendar event.

=back

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

=head2 Common tags for calendar events

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

=over 4

=item * C<d> (required) - unique identifier

=item * C<title> (required) - title of the event

=item * C<summary> (optional) - brief description

=item * C<image> (optional) - URL of an image

=item * C<location> (optional, repeated) - location string

=item * C<g> (optional) - geohash for searchable location

=item * C<p> (optional, repeated) - participant pubkey, relay URL, and role

=item * C<t> (optional, repeated) - hashtag

=item * C<r> (optional, repeated) - reference URL

=item * C<a> (repeated) - reference to kind 31924 calendar

=back

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

lib/Net/Nostr/Calendar.pm  view on Meta::CPAN

C<start> date is inclusive and in ISO 8601 format (YYYY-MM-DD). The
C<end> date is exclusive. If C<end> is omitted, the event ends on the
same date as C<start>. C<start> must be less than C<end> if both are
present (enforced by L</validate>).

=head2 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 L<Net::Nostr::Event>. The
C<start> timestamp is inclusive (Unix seconds). The C<end> timestamp is
exclusive. If C<end> is omitted, the event ends instantaneously.
C<start> must be less than C<end> if both are present (enforced by
L</validate>). C<days> are day-granularity timestamps (C<D> tags)
calculated as C<floor(unix_seconds / 86400)>. The spec requires at
least one C<D> tag (enforced by L</validate>).

=head2 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 L<Net::Nostr::Event>. C<events> is an
arrayref of arrayrefs, each containing a calendar event coordinate and
optional relay URL.

=head2 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 L<Net::Nostr::Event>. C<status> must be
C<accepted>, C<declined>, or C<tentative>. The C<fb> tag is
automatically omitted when C<status> is C<declined>.

=head2 from_event

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

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

=head2 validate

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

Validates a NIP-52 event. Croaks if:

=over

=item * Kind is not 31922, 31923, 31924, or 31925

=item * Kind 31922/31923 missing C<d>, C<title>, or C<start> tag

=item * Kind 31922/31923 C<start> is not less than C<end> (when C<end>
is present)

=item * Kind 31923 missing C<D> tag

=item * Kind 31924 missing C<d> or C<title> tag

=item * Kind 31925 missing C<d>, C<a>, or C<status> tag

=item * Kind 31925 C<status> is not C<accepted>, C<declined>, or
C<tentative>

=back

Returns 1 on success.

=head1 ACCESSORS

=head2 identifier

The C<d> tag value.

=head2 title

The calendar event or calendar title.

=head2 description

The event content. Defaults to C<''>.

lib/Net/Nostr/Calendar.pm  view on Meta::CPAN

Image URL, or C<undef>.

=head2 locations

Arrayref of location strings. Defaults to C<[]>.

=head2 geohash

Geohash string, or C<undef>.

=head2 participants

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

=head2 hashtags

Arrayref of hashtag strings. Defaults to C<[]>.

=head2 references

Arrayref of reference URL strings. Defaults to C<[]>.

=head2 calendars

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

=head2 calendar_events

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

=head2 start_tzid

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

=head2 end_tzid

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

=head2 days

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

=head2 event_coord

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

=head2 event_id

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

=head2 status

RSVP status: C<accepted>, C<declined>, or C<tentative>.

=head2 fb

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

=head2 event_author

Pubkey of the calendar event author (RSVP only).

=head1 SEE ALSO

L<NIP-52|https://github.com/nostr-protocol/nips/blob/master/52.md>,
L<Net::Nostr>, L<Net::Nostr::Event>

=cut



( run in 1.840 second using v1.01-cache-2.11-cpan-98e64b0badf )