# Журнал сессии (2026-05-25) ## Что построили Внутренний мониторинг цен конкурентов на propertyfinder.ae и bayut.com для HOME LIGA REAL ESTATE. **Стек:** FastAPI + Jinja2 (Bootstrap 5) + SQLAlchemy/SQLite + APScheduler + python-telegram-bot + httpx/BS4. **Три процесса** (каждый — свой .bat-лаунчер): - `run_web.bat` — веб-UI на http://127.0.0.1:8000 - `run_bot.bat` — Telegram-бот (polling) - `run_scheduler.bat` — фоновый сканер каждые `SCRAPE_INTERVAL_HOURS` часов ## Эволюция архитектуры ### Первая попытка (отвергнута) **Идея:** сотрудник вводит DLD Permit Number своего объекта → система автоматически ищет «то же permit» на PF и Bayut → находит объявления конкурентов. **Почему не сработало:** 1. В Дубае **каждый брокер получает свой permit на свою публикацию**. Два брокера, рекламирующих одну квартиру = два разных permit. Permit не идентифицирует физический объект — он идентифицирует конкретное объявление конкретного брокера. 2. PF на странице объявления показывает permit **картинкой** через сервис верификации, не plain text (anti-scraping). 3. PF search `?q=` — free-text по названию/описанию, не структурированный фильтр. ### Финальная архитектура **Manual URL list + опциональные подсказки:** 1. Сотрудник создаёт проект (название, тип сделки, владелец, опц. building/bedrooms/sqft). 2. На странице проекта **вручную вставляет URL** объявления конкурента → система делает single-page fetch, парсит `__NEXT_DATA__`, добавляет в трекинг. 3. Если указано здание — кнопка «🔍 Подобрать похожие» ищет на PF/Bayut по `building + bedrooms` и предлагает кандидатов с кнопкой «+ Отслеживать». 4. Каждые 4 часа фоновый сканер делает refetch каждого отслеживаемого URL → детектит: - 📈📉 изменение цены - ❌ удаление (URL отдаёт 404) - ♻️ возвращение из удалённого статуса 5. Уведомления — в Telegram личкой владельцу проекта. ## Модель данных - `Employee` — name, tg_chat_id (опц.), tg_username - `Project` — title, deal_type, owner_id, our_price, building, bedrooms, size_sqft, our_url, dld_permit (все после `owner` — опционально) - `CompetitorListing` — source (PF|Bayut), external_id, url, current_price, status (active|removed), agent_name, agency_name, first_seen, last_seen - `PriceHistory` — listing_id, price, recorded_at ## Как подключиться сотруднику 1. В TG найти бота → отправить `/start`. 2. Отправить `/whoami` → бот пришлёт chat_id. 3. В вебе http://127.0.0.1:8000/employees → найти/создать запись → вставить chat_id → Сохранить. ## Известные ограничения - **PF/Bayut могут блокировать** при частых запросах (видно как `Blocked by site (403/429)` в логах). Решение — увеличить интервал; если уже не помогает — добавить Playwright fallback. - **Подсказки эвристические**: ищем по совпадению building name в title + bedrooms-фильтр. Могут попасть «другие квартиры в том же здании» — поэтому добавление в трекинг через ручное подтверждение. - **Permit как plain text** на PF не отдаётся. Если когда-нибудь понадобится — нужен OCR на verification-image. ## Что в .env ``` TG_BOT_TOKEN=<токен от @BotFather> SCRAPE_INTERVAL_HOURS=4 ADMIN_CHAT_ID= # опц. ``` ## Структура проекта ``` DLD Permit Number/ ├── run_web.bat, run_bot.bat, run_scheduler.bat ├── run_web.py ├── requirements.txt ├── .env (не в git — содержит токен) ├── data/monitor.db (создаётся автоматически) ├── app/ │ ├── config.py ← settings + резолвит относительные SQLite-пути в абсолютные │ ├── db.py, models.py │ ├── web.py ← FastAPI: CRUD проектов, /listings add/delete, /suggest │ ├── bot.py ← /start, /whoami, /list, /check │ ├── scheduler.py │ ├── scrapers/{base,propertyfinder,bayut}.py │ ├── services/{monitor,notifier}.py │ └── templates/ ← projects_list, project_form, project_detail, suggest, employees, base ``` ## Что протестировать в первую очередь 1. Удалить старый `data/monitor.db` (схема изменилась). 2. Запустить три .bat файла. 3. `/start` боту → `/whoami` → chat_id → вписать в Сотрудники. 4. Создать тестовый проект (Aykon City Tower B, 2BR). 5. Вставить URL реального объявления конкурента → проверить, что добавилось с ценой/брокером. 6. Жмякнуть «Подобрать похожие» → посмотреть кандидатов. 7. Жмякнуть «Проверить сейчас» → если цена не менялась, изменений не будет (это нормально); для теста алертов можно поменять цену в БД руками или подождать реального изменения.