You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
48 lines
2.2 KiB
48 lines
2.2 KiB
import type { Event, Filter } from 'nostr-tools' |
|
|
|
function comparableTagValue(tagName: string, value: unknown): string { |
|
const text = String(value).trim() |
|
const tagKey = tagName.toLowerCase() |
|
if (tagKey === 't') return text.toLowerCase() |
|
if ((tagKey === 'p' || tagKey === 'e') && /^[0-9a-f]{64}$/i.test(text)) return text.toLowerCase() |
|
return text |
|
} |
|
|
|
function valuesMatchTag(tagName: string, eventValues: string[], filterValues: unknown[]): boolean { |
|
const allowed = new Set(filterValues.map((v) => comparableTagValue(tagName, v))) |
|
return eventValues.some((v) => allowed.has(comparableTagValue(tagName, v))) |
|
} |
|
|
|
export function eventMatchesLocalFeedFilter(event: Event, filter: Filter): boolean { |
|
if (Array.isArray(filter.ids) && filter.ids.length > 0 && !filter.ids.includes(event.id)) return false |
|
if (Array.isArray(filter.authors) && filter.authors.length > 0) { |
|
const allowedAuthors = new Set(filter.authors.map((author) => author.toLowerCase())) |
|
if (!allowedAuthors.has(event.pubkey.toLowerCase())) return false |
|
} |
|
if (Array.isArray(filter.kinds) && filter.kinds.length > 0 && !filter.kinds.includes(event.kind)) return false |
|
if (typeof filter.since === 'number' && event.created_at < filter.since) return false |
|
if (typeof filter.until === 'number' && event.created_at > filter.until) return false |
|
|
|
const search = typeof filter.search === 'string' ? filter.search.trim().toLowerCase() : '' |
|
if (search) { |
|
const haystack = `${event.content ?? ''} ${(event.tags ?? []).flat().join(' ')}`.toLowerCase() |
|
if (!haystack.includes(search)) return false |
|
} |
|
|
|
for (const [key, values] of Object.entries(filter)) { |
|
if (!key.startsWith('#')) continue |
|
if (!Array.isArray(values) || values.length === 0) continue |
|
const tagName = key.slice(1) |
|
const eventValues = event.tags |
|
.filter((tag) => tag[0]?.toLowerCase() === tagName.toLowerCase() && typeof tag[1] === 'string') |
|
.map((tag) => tag[1] as string) |
|
if (eventValues.length === 0) return false |
|
if (!valuesMatchTag(tagName, eventValues, values)) return false |
|
} |
|
|
|
return true |
|
} |
|
|
|
export function eventMatchesAnyLocalFeedFilter(event: Event, filters: readonly Filter[]): boolean { |
|
return filters.some((filter) => eventMatchesLocalFeedFilter(event, filter)) |
|
}
|
|
|