import { type Event, matchFilters } from '@/deps.ts'; import * as client from '@/client.ts'; import * as eventsDB from '@/db/events.ts'; import { eventDateComparator } from '@/utils.ts'; import type { DittoFilter, GetFiltersOpts } from '@/filter.ts'; /** Get filters from the database and pool, and mix the best results together. */ async function getFilters( filters: DittoFilter[], opts?: GetFiltersOpts, ): Promise[]> { const results = await Promise.allSettled([ client.getFilters(filters.filter((filter) => !filter.local), opts), eventsDB.getFilters(filters, opts), ]); const events = results .filter((result): result is PromiseFulfilledResult[]> => result.status === 'fulfilled') .flatMap((result) => result.value); return unmixEvents(events, filters); } /** Combine and sort events to match the filters. */ function unmixEvents(events: Event[], filters: DittoFilter[]): Event[] { events = dedupeEvents(events); events = takeNewestEvents(events); events = events.filter((event) => matchFilters(filters, event)); events.sort(eventDateComparator); return events; } /** Deduplicate events by ID. */ function dedupeEvents(events: Event[]): Event[] { return [...new Map(events.map((event) => [event.id, event])).values()]; } /** Take the newest events among replaceable ones. */ function takeNewestEvents(events: Event[]): Event[] { const isReplaceable = (kind: number) => kind === 0 || kind === 3 || (10000 <= kind && kind < 20000) || (30000 <= kind && kind < 40000); // Group events by author and kind. const groupedEvents = events.reduce[]>>((acc, event) => { const key = `${event.pubkey}:${event.kind}`; const group = acc.get(key) || []; acc.set(key, [...group, event]); return acc; }, new Map()); // Process each group. const processedEvents = Array.from(groupedEvents.values()).flatMap((group) => { if (isReplaceable(group[0].kind)) { // Sort by `created_at` and take the latest event. return group.sort(eventDateComparator)[0]; } return group; }); return processedEvents; } export { getFilters };