feat(events): Phase 4 — provider adapters for Eventbrite + Meetup

- Add EventProvider interface (base.ts) with fetchEvents(url, name, ctx, config)
- Refactor iCal parser and website extractor as provider adapters
- Add Eventbrite provider: API v3 search by location, category mapping,
  price info extraction. Requires EVENTBRITE_API_KEY env var.
- Add Meetup provider: GraphQL API search by location, topic→category
  mapping, HTML stripping. Requires MEETUP_API_KEY env var.
- Provider registry (getProvider, PROVIDER_TYPES) replaces hardcoded
  switch in crawl-scheduler
- Crawl scheduler now joins sources with regions for ProviderContext
  (lat/lon/radius/label) — platform providers need this for geo-search
- Source creation accepts 'eventbrite' and 'meetup' types (url optional)
- Both providers gracefully return empty when API keys unconfigured

116 tests (all passing), no regressions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-18 15:43:40 +02:00
parent 4d82381737
commit ed801cf725
13 changed files with 708 additions and 44 deletions

View file

@ -95,8 +95,8 @@ export async function getSources(): Promise<DiscoverySource[]> {
}
export async function createSource(input: {
type: 'ical' | 'website';
url: string;
type: 'ical' | 'website' | 'eventbrite' | 'meetup';
url?: string;
name: string;
regionId: string;
crawlIntervalHours?: number;

View file

@ -20,9 +20,11 @@ export interface DiscoveryInterest {
createdAt: string;
}
export type SourceType = 'ical' | 'website' | 'eventbrite' | 'meetup';
export interface DiscoverySource {
id: string;
type: 'ical' | 'website';
type: SourceType;
url: string | null;
name: string;
regionId: string | null;