NAME

Net::Nostr::Community - NIP-72 moderated communities

SYNOPSIS

use Net::Nostr::Community;

# Define a community
my $event = Net::Nostr::Community->community(
    pubkey      => $owner_pubkey,
    identifier  => 'my-community',
    name        => 'My Community',
    description => 'A place for discussion',
    image       => ['https://example.com/banner.jpg', '1200x400'],
    moderators  => [
        { pubkey => $mod_pubkey, relay => 'wss://relay1' },
    ],
    relays      => [
        { url => 'wss://relay.example.com', marker => 'requests' },
    ],
);

# Post to a community (top-level, kind 1111)
my $post = Net::Nostr::Community->post(
    pubkey           => $user_pubkey,
    content          => 'Hello community!',
    community_pubkey => $owner_pubkey,
    community_d      => 'my-community',
);

# Reply to a post (nested, kind 1111)
my $reply = Net::Nostr::Community->reply(
    pubkey           => $user_pubkey,
    content          => 'Great post!',
    community_pubkey => $owner_pubkey,
    community_d      => 'my-community',
    parent_id        => $parent_event_id,
    parent_pubkey    => $parent_author,
    parent_kind      => '1111',
);

# Approve a post (moderator action, kind 4550)
my $approval = Net::Nostr::Community->approval(
    pubkey           => $mod_pubkey,
    community_pubkey => $owner_pubkey,
    community_d      => 'my-community',
    post             => $post_event,
);

# Parse a community or approval event
my $info = Net::Nostr::Community->from_event($event);
say $info->name;  # 'My Community'

DESCRIPTION

Implements NIP-72 moderated communities (Reddit-style). Communities are defined by kind 34550 addressable events that list moderators, relays, and metadata. Users post to communities using kind 1111 (NIP-22) events with community-scoped tags. Moderators approve posts with kind 4550 events.

The d tag of a community definition MAY double as its name, but if a name tag is provided, clients SHOULD display it instead.

Posts use uppercase tags (A, P, K) for root scope (the community) and lowercase tags (a/e, p, k) for the parent. For top-level posts, both sets point to the community itself.

CONSTRUCTOR

new

my $info = Net::Nostr::Community->new(%fields);

Creates a new Net::Nostr::Community object. Typically returned by "from_event"; calling new directly is useful for testing or manual construction.

my $info = Net::Nostr::Community->new(
    identifier  => 'my-community',
    name        => 'My Community',
    description => 'A place for discussion.',
);

Accepted fields: identifier, name, description, image, moderators (defaults to []), relays (defaults to []), communities (defaults to []), post_id, post_coordinate, post_author, post_kind. Croaks on unknown arguments.

CLASS METHODS

community

my $event = Net::Nostr::Community->community(
    pubkey      => $hex_pubkey,           # required
    identifier  => 'my-community',        # required (d tag)
    name        => 'Display Name',        # optional (SHOULD)
    description => 'About this place',    # optional
    image       => ['url', '800x600'],    # optional (with optional dims)
    moderators  => [                      # optional
        { pubkey => $pk, relay => 'wss://...' },
    ],
    relays      => [                      # optional (MAY)
        { url => 'wss://...', marker => 'requests' },
    ],
    extra_tags  => [['rules', '...']],    # optional
);

Creates a kind 34550 community definition Net::Nostr::Event.

Moderator entries become p tags with the moderator role. The relay field is optional per moderator. Relay entries become relay tags with an optional marker (author, requests, approvals).

post

my $event = Net::Nostr::Community->post(
    pubkey           => $hex_pubkey,       # required
    content          => 'Hello!',          # required
    community_pubkey => $community_owner,  # required
    community_d      => 'my-community',    # required
    relay            => 'wss://...',       # optional
);

Creates a kind 1111 top-level post to a community. Both uppercase and lowercase NIP-22 tags point to the community definition, as specified by the NIP.

reply

my $event = Net::Nostr::Community->reply(
    pubkey           => $hex_pubkey,       # required
    content          => 'I agree!',        # required
    community_pubkey => $community_owner,  # required
    community_d      => 'my-community',    # required
    parent_id        => $event_id,         # required
    parent_pubkey    => $parent_pk,        # required
    parent_kind      => '1111',            # required
    relay            => 'wss://...',       # optional
);

Creates a kind 1111 nested reply. Uppercase tags point to the community definition (root scope), lowercase tags point to the parent post or reply.

approval

my $event = Net::Nostr::Community->approval(
    pubkey           => $mod_pubkey,       # required
    community_pubkey => $community_owner,  # required (or use communities)
    community_d      => 'my-community',    # required (or use communities)
    post             => $post_event,       # required
    relay            => 'wss://...',       # optional
    approve_via      => 'e',               # optional: 'e', 'a', or 'both'
);

Creates a kind 4550 approval event. The content is the JSON-stringified post event. The approval MUST include a community a tag, a post reference (e or a tag), the post author's p tag, and a k tag with the post kind.

For replaceable events, approve_via controls how the post is referenced:

e (default) - approve this specific version
a - authorize the author to make future changes
both - approve current version and authorize changes

For approving a post across multiple communities:

my $event = Net::Nostr::Community->approval(
    pubkey      => $mod_pubkey,
    communities => [
        { pubkey => $pk1, d => 'comm1', relay => 'wss://r1' },
        { pubkey => $pk2, d => 'comm2' },
    ],
    post        => $post_event,
);

from_event

my $info = Net::Nostr::Community->from_event($event);

Parses a kind 34550 or 4550 event. Returns a Net::Nostr::Community object with accessors, or undef if the event kind is not recognized.

For kind 34550 (community definition), the returned object has identifier, name, description, image, moderators, and relays accessors.

For kind 4550 (approval), the returned object has communities, post_id, post_coordinate, post_author, and post_kind accessors.

validate

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

Validates that an event is a well-formed NIP-72 event. Croaks if:

  • Kind is not 34550 or 4550

  • Kind 34550 missing d tag

  • Kind 4550 missing community a tag (34550:...)

  • Kind 4550 missing e or a tag for the post

  • Kind 4550 missing p tag for the post author

community_filter

my $filter = Net::Nostr::Community->community_filter(
    identifiers => ['my-community'],
    authors     => [$owner_pk],
);

Returns a hashref filter for querying community definitions.

approval_filter

my $filter = Net::Nostr::Community->approval_filter(
    community => '34550:pubkey:identifier',
    authors   => [$mod_pk],
);

Returns a hashref filter for querying approval events.

legacy_post_filter

my $filter = Net::Nostr::Community->legacy_post_filter(
    community => '34550:pubkey:identifier',
);

Returns a hashref filter for querying legacy kind 1 posts tagged with a community. Clients MAY use this for backwards compatibility but SHOULD NOT create new kind 1 posts for communities.

ACCESSORS

These are available on objects returned by "from_event".

identifier

my $id = $info->identifier;

The d tag value (community definition).

name

my $name = $info->name;  # or undef

The name tag value, or undef. When undef, the identifier MAY be used as a display name.

description

my $desc = $info->description;  # or undef

The community description, or undef.

image

my $img = $info->image;  # ['url', '800x600'] or ['url'] or undef

Arrayref of the image URL and optional dimensions, or undef.

moderators

my $mods = $info->moderators;
# [{ pubkey => '...', relay => '...' }, ...]

Arrayref of hashrefs, each with pubkey and optional relay.

relays

my $relays = $info->relays;
# [{ url => 'wss://...', marker => 'requests' }, ...]

Arrayref of hashrefs, each with url and optional marker.

communities

my $comms = $info->communities;  # ['34550:pk:id', ...]

Arrayref of community coordinates (from approval events).

post_id

my $id = $info->post_id;  # or undef

The approved post's event ID (from e tag), or undef.

post_coordinate

my $coord = $info->post_coordinate;  # or undef

The approved post's event coordinate (from non-community a tag), or undef.

post_author

my $pk = $info->post_author;

The approved post author's pubkey (from p tag).

post_kind

my $kind = $info->post_kind;  # '1111'

The approved post's kind (from k tag).

SEE ALSO

NIP-72, Net::Nostr::Comment, Net::Nostr, Net::Nostr::Event