NAME

Net::Nostr::Article - NIP-23 long-form content

SYNOPSIS

use Net::Nostr::Article;

my $pubkey = 'aa' x 32;

# Create an article (kind 30023)
my $event = Net::Nostr::Article->article(
    pubkey       => $pubkey,
    content      => "# My Article\n\nMarkdown content here.",
    identifier   => 'my-article',
    title        => 'My Article',
    summary      => 'A short summary.',
    image        => 'https://example.com/banner.png',
    published_at => 1296962229,
    hashtags     => ['nostr', 'blog'],
);

# Create a draft (kind 30024, same structure)
my $draft = Net::Nostr::Article->draft(
    pubkey     => $pubkey,
    content    => "# WIP\n\nNot finished yet.",
    identifier => 'my-draft',
    title      => 'Work in Progress',
);

# Parse article metadata from an event
my $info = Net::Nostr::Article->from_event($event);
say $info->title;        # 'My Article'
say $info->identifier;   # 'my-article'
say $info->published_at; # '1296962229'

# Generate an naddr for linking
my $naddr = Net::Nostr::Article->to_naddr($event,
    relays => ['wss://relay.example.com'],
);

# Validate an article event
Net::Nostr::Article->validate($event);

DESCRIPTION

Implements NIP-23 long-form content (articles and drafts). Articles are kind 30023 addressable events with Markdown content and optional metadata tags. Drafts are kind 30024 with the same structure.

Content should be Markdown text. Clients creating articles MUST NOT hard line-break paragraphs and MUST NOT include HTML in the Markdown.

Articles are editable via the d tag identifier. The created_at field represents the date of the last update. Use the published_at tag for the original publication date.

References to other Nostr entities in the content should use nostr: URIs (NIP-21).

Replies to articles MUST use NIP-22 kind 1111 comments (see Net::Nostr::Comment).

CONSTRUCTOR

new

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

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

my $info = Net::Nostr::Article->new(
    identifier => 'my-article',
    title      => 'My Article',
    summary    => 'A brief overview.',
    image      => 'https://example.com/cover.jpg',
);

Accepted fields: identifier, title, image, summary, published_at, hashtags (defaults to []). Croaks on unknown arguments.

CLASS METHODS

article

my $event = Net::Nostr::Article->article(
    pubkey       => $hex_pubkey,         # required
    content      => $markdown,          # required
    identifier   => 'article-slug',     # required (d tag)
    title        => 'Article Title',    # optional
    image        => 'https://...',      # optional
    summary      => 'Short summary.',   # optional
    published_at => 1296962229,         # optional (unix timestamp)
    hashtags     => ['topic1'],         # optional (t tags)
    extra_tags   => [['e', $id, $r]],   # optional (additional tags)
    created_at   => time(),             # optional
);

Creates a kind 30023 article Net::Nostr::Event. pubkey, content, and identifier are required. All metadata fields are optional.

The published_at value is stringified in the tag per spec. The extra_tags parameter accepts an arrayref of additional tags (e.g. e, a, p tags for references).

my $event = Net::Nostr::Article->article(
    pubkey     => 'aa' x 32,
    content    => "# Hello\n\nWorld.",
    identifier => 'hello-world',
    title      => 'Hello',
    hashtags   => ['greeting'],
);

draft

my $event = Net::Nostr::Article->draft(
    pubkey     => $hex_pubkey,
    content    => $markdown,
    identifier => 'draft-slug',
    # same optional params as article()
);

Creates a kind 30024 draft event. Accepts the same parameters as "article". Drafts have the same structure as articles but are not intended for publication.

from_event

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

Parses article metadata from a kind 30023 or 30024 Net::Nostr::Event. Returns a Net::Nostr::Article object with accessors, or undef if the event is not an article or draft kind.

my $info = Net::Nostr::Article->from_event($event);
say $info->identifier;   # 'my-article'
say $info->title;        # 'My Article' or undef
say $info->hashtags;     # ['nostr', 'blog']

validate

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

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

  • Kind is not 30023 or 30024

  • Missing d tag

eval { Net::Nostr::Article->validate($event) };
warn "Invalid article: $@" if $@;

to_naddr

my $naddr = Net::Nostr::Article->to_naddr($event,
    relays => ['wss://relay.com'],
);

Generates a NIP-19 naddr bech32 string for linking to the article. The relays parameter is optional.

my $naddr = Net::Nostr::Article->to_naddr($article_event);
# naddr1...

ACCESSORS

These are available on objects returned by "from_event".

identifier

my $id = $info->identifier;

The d tag value identifying the article.

title

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

The article title, or undef if not set.

image

my $url = $info->image;  # or undef

URL of the article's header image, or undef.

summary

my $text = $info->summary;  # or undef

The article summary, or undef.

published_at

my $ts = $info->published_at;  # '1296962229' or undef

The original publication timestamp (stringified unix seconds), or undef.

hashtags

my $tags = $info->hashtags;  # ['nostr', 'blog']

Arrayref of hashtag strings from t tags. Empty arrayref if none.

SEE ALSO

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