NAME

Net::Nostr::Filter - Nostr event filter for subscriptions and queries

SYNOPSIS

use Net::Nostr::Filter;

my $filter = Net::Nostr::Filter->new(
    kinds   => [1],
    authors => ['a' x 64],
    since   => time() - 3600,
    limit   => 50,
);

# Check if an event matches
if ($filter->matches($event)) { ... }

# Tag filters use #<tag-name> syntax
my $filter = Net::Nostr::Filter->new(
    '#t' => ['nostr', 'perl'],
);

# Multi-letter tag names are supported
my $filter = Net::Nostr::Filter->new(
    '#overnet_et' => ['chat'],
);

# Retrieve a tag filter
my $values = $filter->tag_filter('t');  # ['nostr', 'perl']

# Multiple filters act as OR conditions
my $f1 = Net::Nostr::Filter->new(kinds => [1]);
my $f2 = Net::Nostr::Filter->new(kinds => [0]);
if (Net::Nostr::Filter->matches_any($event, $f1, $f2)) { ... }

DESCRIPTION

Implements Nostr event filtering as defined by NIP-01, with NIP-50 search support. All conditions within a single filter are AND-ed together. Multiple filters in a subscription are OR-ed (use matches_any).

CONSTRUCTOR

new

my $filter = Net::Nostr::Filter->new(
    ids     => ['a' x 64],
    authors => ['b' x 64],
    kinds   => [1, 2],
    since   => 1673361254,
    until   => 1673361999,
    limit   => 100,
    search  => 'best nostr apps',
    '#e'    => ['c' x 64],
    '#p'    => ['d' x 64],
    '#t'    => ['nostr'],
);

All fields are optional. ids, authors, #e, and #p values must be 64-character lowercase hex strings. kinds values must be integers between 0 and 65535. since, until, and limit must be non-negative integers. search must be a plain string (not a reference). Tag filter keys use the form #<name> where the name starts with a letter and may contain letters, digits, and underscores (e.g. #t, #e, #overnet_et). Croaks on invalid values or unknown arguments.

The search field (NIP-50) is a human-readable query string. It may contain key:value extension pairs (e.g. language:en). See "parse_search_extensions".

METHODS

matches

my $bool = $filter->matches($event);

Returns true if the event matches all conditions in this filter. When search is set, performs case-insensitive matching of all search terms against the event's content field. Extension pairs (key:value) in the search string are ignored for client-side matching.

my $filter = Net::Nostr::Filter->new(kinds => [1], since => 1000);
my $event = Net::Nostr::Event->new(
    pubkey => 'a' x 64, kind => 1, content => '',
    created_at => 2000, tags => [],
);
say $filter->matches($event);  # 1

# NIP-50: search matching
my $search_filter = Net::Nostr::Filter->new(search => 'nostr apps');
my $note = Net::Nostr::Event->new(
    pubkey => 'a' x 64, kind => 1, content => 'best nostr apps for daily use',
    created_at => 1000, tags => [],
);
say $search_filter->matches($note);  # 1

matches_any

my $bool = Net::Nostr::Filter->matches_any($event, @filters);

Class method. Returns true if the event matches any of the given filters (OR logic).

tag_filter

my $values = $filter->tag_filter('t');  # ['nostr'] or undef

Returns the arrayref of values for a tag filter, or undef if that tag name was not specified.

to_hash

my $hash = $filter->to_hash;
# { kinds => [1], authors => [...], '#t' => ['nostr'] }

Returns a hashref suitable for JSON encoding in a REQ message. Only includes fields that were set.

ids

my $ids = $filter->ids;  # arrayref or undef

Event id prefixes to match. Events whose id starts with any of these hex strings are included.

authors

my $authors = $filter->authors;  # arrayref or undef

Pubkey prefixes to match. Events whose pubkey starts with any of these hex strings are included.

kinds

my $kinds = $filter->kinds;  # arrayref or undef

Event kinds to match. Events with any of these kinds are included.

since

my $since = $filter->since;  # Unix timestamp or undef

Lower bound (inclusive) on created_at.

until

my $until = $filter->until;  # Unix timestamp or undef

Upper bound (inclusive) on created_at.

limit

my $limit = $filter->limit;  # integer or undef

Maximum number of events to return. Applied after sorting by created_at descending.

my $search = $filter->search;  # string or undef

NIP-50 search query string. See "parse_search_extensions" for parsing extension pairs from the search string.

parse_search_extensions

my $result = Net::Nostr::Filter->parse_search_extensions(
    'best nostr apps language:en nsfw:false'
);
# $result->{terms}      = ['best', 'nostr', 'apps']
# $result->{extensions} = { language => 'en', nsfw => 'false' }

Class method. Parses a NIP-50 search string and separates plain search terms from key:value extension pairs. Returns a hashref with terms (arrayref) and extensions (hashref).

Supported extensions defined by NIP-50: include:spam, domain:<domain>, language:<code>, sentiment:<value>, nsfw:<bool>.

SEE ALSO

NIP-01, NIP-50, Net::Nostr, Net::Nostr::Event