Problém
Hľadanie bytu v Bratislave znamená každý deň prechádzať tie isté portály, vidieť ten istý byt viackrát od rôznych realitiek a len hádať, ako dlho bude trvať cesta do práce — inzeráty zvyčajne nepovedia viac než „blízko centra“.
Navyše dva najväčšie slovenské portály v obchodných podmienkach výslovne zakazujú automatické získavanie dát, takže zrejmé riešenie — scraper — neprichádzalo do úvahy.
Riešenie
FlatWatch využíva prístup, ktorý portály samy ponúkajú: e-mailové upozornenia na uložené vyhľadávanie. Worker na pozadí tieto upozornenia číta, stiahne každú novú ponuku, vyčistí ju, odhalí duplikáty, vypočíta skutočný čas pešo a MHD ku kotviacemu bodu, ohodnotí byt podľa mojich filtrov a pošle mi e-mail, keď sa objaví niečo dobré.
Všetko beží self-hosted — geokódovanie, trasy, MHD aj mapové dlaždice — takže netreba spravovať žiadne účty u externých API ani limity.
Ako to funguje
- Príjem — worker číta upozornenia cez IMAP, vytiahne z nich odkazy na ponuky a stiahne detail každej novej alebo zmenenej ponuky.
- Normalizácia a duplikáty — ponuky sa prevedú na jeden dátový model a porovnajú podľa šiestich signálov: súradnice, počet izieb, plocha, cena, podobnosť textu (
pg_trgm) a perceptuálny hash fotiek. - Skutočný čas cesty — adresy geokóduje Nominatim, pešie trasy počíta OSRM a MHD OpenTripPlanner 2 z oficiálnych cestovných poriadkov DPB (GTFS).
- Skóre a upozornenia — každá ponuka dostane skóre zhody podľa nastaviteľných váh; zhoda spustí okamžitý e-mail a denný prehľad.
- Dashboard — mapa s ponukami a trasami, filtre aj vybraná ponuka uložené v URL, galéria fotiek a nastavenia kotviaceho bodu, zdrojov, upozornení a váh skóre.
Screenshoty aplikácie
Technické výzvy
Legálny prístup k dátam
Scraping vylúčili obchodné podmienky portálov. Postaviť príjem dát na e-mailových upozorneniach zachovalo hlavnú myšlienku — automatické zachytenie nových ponúk — a zároveň ostať v rámci toho, čo každý portál dovoľuje. Zdroje sú adaptéry za jedným rozhraním, takže ďalší portál znamená nový súbor, nie prepisovanie.
Ten istý byt, inzerovaný mnohokrát
Realitky ten istý byt opakovane pridávajú s trochu iným nadpisom, cenou a fotkami. Detekcia duplikátov kombinuje štrukturálne signály s trigramovou podobnosťou textu v PostgreSQL a perceptuálnym hashom fotiek, takže opakované inzeráty skončia v jednej skupine a nezahltia zoznam.
Deadlock odhalený pri ostrom behu
Prvý beh proti skutočnej schránke sa navždy zasekol. Označenie správy ako prečítanej zvnútra streamovaného sťahovania viacerých správ sa nikdy nedokončí: spojenie čaká, kým stream skončí, a stream čaká na zaseknutý príkaz. Potvrdil som to postupným zužovaním (bisecting) a opravil sťahovaním po jednej správe, pričom stream sa vždy úplne vyprázdni pred ďalším príkazom. Ten istý beh odhalil aj chybu plánovača — zaseknutý beh nechal zdroj navždy „na rade“, takže sa spúšťali prekrývajúce sa behy — teraz plánovač preskočí zdroje, ktoré ešte bežia.
Technológie a architektúra
pnpm monorepo s Next.js dashboardom a samostatným dlhodobo bežiacim Node.js workerom, ktoré zdieľajú jednu databázu PostgreSQL spravovanú cez Drizzle. Čistá doménová logika (normalizácia, skóre, duplikáty) je v samostatnom balíku bez I/O, takže sa ľahko testuje.
- Frontend — Next.js, React, TypeScript, MapLibre so self-hosted vektorovými dlaždicami (svetlý aj tmavý štýl mapy)
- Backend — Node.js worker (príjem cez IMAP, plánovanie, upozornenia), Server Actions
- Dáta — PostgreSQL, Drizzle ORM, geo dotazy cez
earthdistance/cube,pg_trgm - Trasy a geo — OSRM, OpenTripPlanner 2 (GTFS DPB), Nominatim
- Infraštruktúra — Docker Compose na jednom serveri, prístup cez spoločné heslo
Výsledok
FlatWatch funguje od začiatku do konca na skutočných dátach: skutočné upozornenia sa spracujú, nové ponuky sa stiahnu, zbavia duplikátov, geokódujú a naplánujú trasy podľa skutočnej bratislavskej MHD a e-maily s upozorneniami — vrátane denného prehľadu — prichádzajú samy. Používam ho pri vlastnom hľadaní bytu; ďalším krokom je pridanie druhého zdroja ponúk.