---
name: torgiradar_overview
description: "«Торги без риска» — продукт лотов с торгов (backend :20152, бот @TorgiBezRiska_bot, сайт lots.zbrcheck.ru:20151). ⚠️ БРОНЬ СНЯТА 08.2026, продаётся ЭКСПЕРТНЫЙ отчёт по участку (REPORT_PRICE 599 ₽, 2 бесплатных при регистрации). Всё «бронь»/«AI-отчёт» ниже — история"
metadata: 
  node_type: memory
  type: project
  originSessionId: 04f2b3b2-6c7a-4097-90f0-ec6f20a0ba67
  modified: 2026-08-17T07:21:53.941Z
---

**⚠️⚠️ 2026-08 — МОДЕЛЬ ПРОДУКТА СМЕНИЛАСЬ. Читать ПЕРЕД всем, что ниже.**
**Брони больше нет** — как продукт снята: `tbr/config.py:78` «BOOKING_FEE удалён вместе с бронью —
цена продукта REPORT_PRICE», `:115` «до 08.2026 наградой были брони — продукт снят». Что сейчас:
человек находит на карте лот дешевле рынка и **до подачи заявки** покупает **экспертный отчёт**
по участку (`REPORT_PRICE` = 599 ₽, при регистрации `FREE_REPORTS_ON_SIGNUP` = 2 бесплатных).
Землёй мы не торгуем — продают государство и конкурсные управляющие (так и написано на старте бота).
**«AI-отчёт» переименован в экспертный 01.08.2026.** Всё, что НИЖЕ про бронь, бонус-брони,
success fee и AI-отчёт — **история**, читать как контекст, а не как текущий продукт.
Остатки слова «брон» в коде (`models.py`, `booking.py`, InvId-смещения, sentinel-владельцы) —
служебная проводка под старые записи, не признак живой функции.

**17.08.2026 — вычищены два последних пользовательских следа брони.** `ROBOKASSA_DESCRIPTION`
был «Бронь земельного лота с торгов» — то есть покупатель платил за отчёт, а в описании платежа
видел бронь; переменная нигде не переопределена, значит жил именно этот дефолт. Приведён к тому же
значению, что в «Земле» — **«Консультационные услуги»** (там оба поля такие:
`zemlya-web/backend/app/config.py:93` и `:106`), решение владельца. Соседний
`ROBOKASSA_RECEIPT_PRODUCT_NAME` аудит 01.08 уже поправил, а этот пропустил. Второе: сообщение при
сбое torgi.gov.ru обещало «Бронь по-прежнему доступна» → теперь «Лоты и экспертные отчёты
по-прежнему доступны». Бэкапы `*.bak-desc-20260817`. Бот перезапущен, поллинг идёт.
См. [[ceberspost_overview]] — тот же протухший факт жил и в контент-машине.

**🔁 2026-06-23 — ТЕХНИЧЕСКИЙ РЕНДЕЙМ `torgiradar` → `tbr` (полное искоренение, commit tbr `619c079`).** Канон ТЕПЕРЬ: каталог `~/Services/tbr` (пакет `tbr/`), фронт `~/Services/tbr-frontend`, БД `~/Services/tbr/data/db/tbr.db` (env `TBR_DB_PATH`), launchd `local.tbr-{web-api,bot,frontend,pipeline,backup,torgi-monitor}`, env-ключи `TBR_*`, rathole-сервисы `tbr-frontend`/`tbr-api` (порты 18151/18152 не менялись), Ext-фолбэк `/Volumes/Ext/Services/tbr`, gdrive `Tbr-Backups`. Мост zemlya-bot `robokassa_server.py` пишет бронь в `~/Services/tbr/data/db/tbr.db`. Откат старого бэкенда: `~/.rollback-torgiradar-20260623` (удалить после суток). **Всюду НИЖЕ «torgiradar» = старое имя, читать как `tbr`; пути/метки брать из этого баннера.** Подробности → Obsidian `Торги без риска/Changelog.md`. Запуск ботом/CLI: `TBR_DB_PATH=~/Services/tbr/data/db/tbr.db PYTHONPATH=~/Services/tbr:~/Services/zemlya-bot venv/bin/python3 -m tbr.<...>`.

**⚡ 2026-06-18 — РАЗДЕЛЁН В САМОСТОЯТЕЛЬНЫЙ ПРОДУКТ «Торги Радар» (отменяет D16 «единый бренд zbrcheck»; полн. → Obsidian `ТоргиРадар/Changelog.md`+`Decisions.md` 2026-06-18).** Лоты — отдельный стек.
**✅ CUTOVER СДЕЛАН (2026-06-18, по гринлайту юзера, ДО запуска сайта):** zbrcheck (zemlya-web) ПОЛНОСТЬЮ очищен от лотов — commit `fd9a147`. Удалено: фронт-секция/виджеты(LotFeed/Ticker/MyBookings)/страницы(/dashboard/lots,/admin/lots)/меню, BFF `routes/lots.py`, лот-поллер (`lot_payment_poller`/`lot_poller_main`, launchd `local.zemlya-lot-poller` выгружен), `/admin/lot-bookings`, booking-функции в zemlya-web `robokassa.py`, `/api/lots` из antibot-bypass. **Сохранено:** общесайтовый тумблер Robokassa → выделен в `/admin/payment-mode` (он влияет на ВСЕ платежи сайта, не только брони). **НЕ тронут zemlya-bot:** Pro-мост `/internal/lot-pro-report` + вебхук бот-броней `_handle_lot_booking` (инфра живого бота). Проверено: build ok, /api/lots нет, /admin/payment-mode 200, скрины zbrcheck.ru desktop+mobile — вёрстка цела. Веб-доступ к лотам ~сутки только через бот, пока сайт ждёт DNS.
- **Backend (готов, проверен curl):** тот же сервис `local.lotbot-web-api` :20152 расширен до самостоятельного. Своя БД-сущность `lot_users`+`lot_email_tokens`, вход email magic-link (cookie `tr_session`, HMAC-JWT, домено-независимо из env `TORGIRADAR_WEB_BASE_URL`). `/api/regions`, `/api/lots/feed?region=`, бронь от lot_user (`bookings.lot_user_id` авторитет, sentinel -(5_000_000+id)), Robokassa перенесён в `torgiradar/web/` (robokassa+payment_mode+payment_poller в lifespan), `/api/admin/*` (гейт=`config.ADMIN_IDS`). Доустановлены `eval_type_backport`,`httpx`. Секреты в `torgiradar/.env`.
- **AI-мост (готов, проверен; деливерабл = AI-отчёт, не Pro):** `bot/services/robokassa_server.py` `POST/GET /internal/lot-ai-report` (новое имя с 2026-06-23; старый `/internal/lot-pro-report` — алиас); X-Internal-Token=`ZW_INTERNAL_TOKEN`; вставляет `reports(report_mode='ai', user_id=178 сервисный, is_paid=0)` → добивает `web_report_worker`; **is_paid=0 ⇒ AI-отчёт бесплатен, баланс не трогается**. Клиент `torgiradar/web/ai_report.py` (бывш. `web/pro.py`): `request_ai_report_for_booking/fetch_ai_report_result/confirm_paid_and_ai_report`; колонка `bookings.ai_report_id` (бывш. `pro_report_id`); эндпоинт `/api/booking/{id}/ai-report`. Сервис-юзер = users.id **178** (tg -777001).
- **Бот (готов, проверен живым TG):** @TorgiBezRiska_bot (бренд «📡 Торги без риска»; в старых заметках мог фигурировать @TorgiRadarZBR_bot/@LotBot_Cebers_bot — сверять с .env `TORGIRADAR_BOT_USERNAME`), карточки лотов с «📌 Забронировать», book→бонус-confirm→раскрытие→**бесплатный AI-отчёт в чате**. cb_pay — РЕАЛЬНАЯ Robokassa-оплата (ссылка+receipt+IsTest) + подтверждение через Result-вебхук (бронь-InvId≥3M отдельной веткой ПЕРЕД пакетной 2M; вебхук raw-sqlite UPDATE статуса). **Живой воркер бота** `_booking_payment_worker` (30с) подхватывает paid-брони без `ai_report_id` → `confirm_paid_and_ai_report` → reveal full_card + `_deliver_ai_report`. Проверено E2E (2026-06-23): booking→paid→reveal+AI-отчёт за ~20с.
- **✅ Сайт LIVE (2026-06-19): https://torgiradar.zbrcheck.ru** — Next 14 `~/Services/torgiradar-frontend` (дизайн zbrcheck + акцент радар-синий #1E7FB5), `/api/*`→rewrite на :20152. Деплой по `deploy/DEPLOY.md`: launchd `local.torgiradar-frontend` (:20151) → rathole client `torgiradar-frontend`/`torgiradar-api` (мини client.toml) → VPS server.toml bind 18151/18152 → Caddy vhost `torgiradar.zbrcheck.ru` (/api/*→18152, остальное→18151, авто-TLS). Проверено: / и /login 200, /api/regions 493 лота, TLS ок, скрины desktop+mobile — вёрстка цела, zbrcheck.ru+pay не задеты (все 5 rathole-сервисов reconnect). **TG-вход на сайте:** виджет one-click + auto-fallback на deeplink ([[tg_login_widget_fallback]]) — fallback работает сейчас; для one-click осталось BotFather `/setdomain torgiradar.zbrcheck.ru` @TorgiRadarZBR_bot ✅ СДЕЛАНО (one-click виджет включён). **Ещё от юзера (опц.):** redirect-URI домена в Яндекс/Mail (на старте вход = email + Telegram).

**ТоргиРадар** (исходный recon ниже, частично устарел) — ответвление от zemlya ([[zemlya_web_overview]]). Бот ловит новые **земельные** лоты-продажи с torgi.gov.ru, оценивает по рынку (аналоги Яндекс.Недвижимости), и шлёт подписчикам карточки лотов с дисконтом ≥ порога. Монетизация: бронь (фикс ~2990₽, лочит лот на 5 мин) + success fee 15-20% от выгоды при подтверждённой победе (сверка через публичные протоколы torgi).

**Решения (2026-06-04):** только земля MVP; цена по аналогам (Avito/ЦИАН/Яндекс, БЕЗ Домклика); только бронь+success fee (без подписки, лента бесплатна); отдельный бот + общая БД/либы zemlya; всё сетевое — на mac mini.

**Recon Phase 0 (проверено с mini):**
- torgi API `/new/api/public/lotcards/search` + `/lotcards/{id}` работает. Фильтр: `biddType=ZK`+`typeTransaction=sale`+`priceMin>0`. Поля: CadastralNumber (связка с zemlya), SquareZU, PermittedUse, subjectRFCode, category, estateAddress (только в detail — прячем до оплаты).
- torgi гео-блок не-РФ IP → только mini; нужен Russian Trusted Root CA; системный LibreSSL падает → через Python.
- Antibot: Avito 403, ЦИАН 403, **Яндекс.Недвижимость 200** (парсится INITIAL_STATE+ld+json) → MVP на Яндексе, остальные Phase 2 (Playwright/прокси).

**Phase A discovery (D8, проверено вживую):** движок оценки УЖЕ существует как сервисы zemlya-bot в `~/Services/zemlya-bot/bot/services/` — `torgi.py` (готовый клиент torgi, search_lots_by_region/search_by_cadastral/_parse_lot, ssl=False), `yandex_realty` (аналоги), `ai_valuation.analyze_property_valuation` (LLM-оценка), `land_constraints`, `oks.find_oks_on_parcel` (дом на участке), `nspd`, `cian`/`avito` (Phase2). Переиспользуем СЕРВИСНЫЙ слой через PYTHONPATH (НЕ handler cadastral.py 9386 строк, НЕ рефакторим прод). Пишем: поллер (свой запрос: lotStatus=PUBLISHED+biddType=ZK+typeTransaction=sale, CadastralNumber из characteristics) + продукт (подписка/карточки/бронь/оплата/протоколы) + БД-схема.

**Решено юзером:** имя=ТоргиРадар, порог дисконта=35%. Источники аналогов — все три (Яндекс MVP, Avito/ЦИАН Phase2). Юрлицо для Robokassa — уточнить на Phase D.
**Код:** zemlya на mini `~/Services/zemlya-bot` (SQLite `data/db/zemlya_bot.db`, НЕ Postgres! aiogram 3.22/SQLAlchemy 2.0/py3.9.6) и `~/Services/zemlya-web/backend`. ТоргиРадар: локально `~/Разработки/torgiradar`, на mini `~/Services/torgiradar` (rsync). Запуск под venv zemlya: `PYTHONPATH=$PWD:$HOME/Services/zemlya-bot $HOME/Services/zemlya-bot/venv/bin/python3 -m torgiradar.run_poll`.

**Phase A фундамент готов (v0.1.0-dev, проверено вживую):** config/db(SQLite+WAL, отдельный файл — D4 испр.: zemlya на SQLite, не шарим)/models(6 таблиц lots/valuations/subscribers/alerts/bookings/deals)/torgi_poller(structured: biddType=ZK+активные+sale+цена>0+кадастр из characteristics, detail→адрес)/run_poll CLI. Live: 1 целевой лот сохранён с адресом. Выход низкий (1/20, остальное аренда/IPS) — расширить охват потом.
**БД на внешнем диске mini (D9):** `/Volumes/Ext/Services/torgiradar/data/db/torgiradar.db` (конвенция как avtero, env TORGIRADAR_DB_PATH override). `/Volumes/Ext` = 954ГБ внешний диск = «вторая память» mini, тут хранят данные сервисов. Зависит от маунта.
**valuation.py готов (проверено вживую):** переиспускает zemlya yandex_realty.search_listings (Playwright), медиану ₽/м²+дисконт считаем сами, REGION_CODES скопирован. _parse_area zemlya нормализует в м² (юнит-ловушка снята). Alert при дисконте≥35%+high. Запуск из ~/Services/zemlya-bot CWD (для .env+Playwright): `PYTHONPATH=$HOME/Services/torgiradar:$PWD venv/bin/python3 -m torgiradar.run_valuation`. Live: лот Новгородской 1141м² торги 282k → 22 аналога → рынок 188k → дисконт −50% → корректно не в ленту.
**Токен бота:** @TorgiRadarZBR_bot (постоянный, id 8846112470; старый @LotBot_Cebers_bot отвязан 2026-06-19, токен отозван) на mini `~/Services/torgiradar/.env` (chmod600, вне git). Telegram с mini через proxy `http://127.0.0.1:2081` (xray SELECTIVE_PROXY_TELEGRAM). Прод → нейтральный username.
**Бот MVP готов + e2e через реальный TG (проверено):** `torgiradar/bot/` aiogram 3.22 через proxy 2081. /start подписка, /demo карточка, бронь (race-safe лок 5мин, expiry-worker), оплата-ЗАГЛУШКА [PAYMENT-STUB] (pay→paid+Deal, реальная Robokassa Phase D), раскрытие адреса/кадастра/ссылки. cards.py/booking.py/handlers.py/main.py. Запуск: `cd ~/Services/torgiradar && PYTHONPATH=$PWD:$HOME/Services/zemlya-bot $HOME/Services/zemlya-bot/venv/bin/python3 -m torgiradar.bot.main`. Сейчас крутится вручную (nohup), НЕ launchd.
**E2E:** `tests/e2e/flow_booking.py` через tg-web-e2e (acc 8688355181). **УРОК:** колоночные inline-кнопки (оплата/отмена) ненадёжно кликаются unscoped `.last` в TG-web (виртуализация DOM) → клик надёжен только scoped по `data-mid` пузыря; однокнопочные ряды ок. Скриншоты `docs/e2e/`.
**D10 — бот показывает ТОЛЬКО выгодные лоты:** `/lots` (быв./demo) = confidence=high И discount≥35%, сортировка по дисконту. Переоценённые/аномальные скрыты. Проверено в TG (Пермь ИЖС −75% показан, Новгород −64% скрыт).
**D11 — locality-оценка РЕАЛИЗОВАНА (проверено):** корень фейковых 96% — `yandex_realty._build_search_url` для немаппленных регионов транслитерирует→невалидный slug→Яндекс отдаёт МОСКВУ (Марий-Эл сравнивался с Москвой 5-78млн). Фикс 3 уровня в valuation.py: (1) координаты по кадастру `nspd.search_by_cadastral`→center_lat/lon + max_distance_km=50; (2) рантайм-расширение `yr.REGION_SLUGS.update(REGION_SLUGS_EXT)` ~30 областей/краёв (файл zemlya НЕ трогаем); (3) сейфти `_filter_in_region` — аналог обязан быть в регионе по адресу (защита от дефолта-Москвы→мало аналогов→low→нет ложного алерта). Результат: Новгород −38%(дороже)скрыт, Пермь/Губаха −56% в ленте (реальный), Марий-Эл 0/40→скрыт. TODO: slug'и республик/АО нерегулярны (пока safe-skip). SUSPICIOUS_DISCOUNT_CAP=0.90 как доп.страховка region-метода.
**D11.1 — тиринг (выбор юзера, проверено):** `max_distance_km`/`location_queries` Яндекса = no-op (объявления без координат, 10км=50км идентично); малые нас.пункты без аналогов (Губаха 0). Тиринг в valuation.py (`_locality_keywords`/`_filter_by_stems`): **local** (≥3 аналога в районе/городе по стемам адреса → confidence=high → авто-лента/алерты) → **region** фолбэк (confidence=region, НЕ авто-рассылается, в /lots с пометкой «≈по региону, ориентировочно») → **low** (скрыт). save_valuation алертит ТОЛЬКО high; /lots показывает high+region; anon_card метит региональные. Проверено e2e: Новгород local(15)−38%скрыт, Пермь region(14)−56% с пометкой, Марий-Эл low. MAX_DISTANCE_KM=10 в конфиге но фактически не влияет.
**D11.2 — показываем ТОЛЬКО достоверно оценённые (уточнение юзера):** региональная «ориентировочная» оценка = «не смогли нормально оценить» → не показываем. `/lots`+лента = строго confidence='high' (локальная) + recommended_price NOT NULL + дисконт≥порог. Региональный тир вычисляется internal но не показывается/не рассылается. Следствие (принято): мало алертов = «качество>покрытие». Текущий инвентарь → /lots «пока нет» (Новгород дороже, Пермь региональная, Марий low).
**D12 — АВТОНОМНАЯ РАБОТА под launchd (проверено):** 3 сервиса в `~/Library/LaunchAgents/` (плисты в репо `deploy/`): `local.torgiradar-bot` (KeepAlive, бот+broadcast-воркер 30с), `local.torgiradar-pipeline` (StartInterval 1800, poll+оценка, CWD=zemlya-bot), `local.torgiradar-backup` (86400, sqlite .backup с Ext → rclone `gdrive:TorgiRadar-Backups`, retention 30). **КЛЮЧЕВОЙ ТРЮК:** launchd venv-python (→Xcode-python) получал `authorization denied` на Ext (TCC removable-volume по-бинарно), Xcode-python в FDA через GUI не добавить (серый). РЕШЕНИЕ: запуск через `/bin/bash -c "python ..."` — у /bin/bash есть FDA, дочерний python наследует доступ к Ext. Так **live-БД осталась на Ext** (выбор юзера), бэкапы → gdrive. rclone `/opt/homebrew/bin/rclone`. **Broadcast проверен e2e** (карточка доставлена подписчику). Grep-grace: BSD head ≠ `-n -N` (retention через подсчёт). Управление: `launchctl load/unload/kickstart -k gui/$(id -u)/local.torgiradar-bot`.
**D13 — АДМИН-ПАНЕЛЬ (проверено e2e реальным TG, 2026-06-05):** `bot/admin.py` отдельный `admin_router` (в main.py подключён ПЕРВЫМ). Gating: `config.OWNER_ID=1005954706` + `ADMIN_IDS` из env `TORGIRADAR_ADMIN_IDS` (CSV), фильтр на роутере `F.from_user.id.in_(ADMIN_IDS)`. Взяты ПАТТЕРНЫ zemlya (gating/inline edit-in-place/`/admin`/аудит), НЕ код (zemlya admin.py 9182 стр под чужой домен). Разделы: 📊 Статистика, 🔥 Лоты (+«Разослать сейчас»=`broadcast_new_alerts`), 📋 Сделки ([✅Победа/❌Не выиграл/↩️Сброс]; Победа→`status=won`+`success_fee_amount`=`rate×(рынок−торги)`+аудит), 📢 Рассылка (FSM `AnnounceSG` текст→предпросмотр→подтв/отмена), ⚙️ Настройки (+«Запустить пайплайн» через `launchctl kickstart`). Новая lite-таблица `AdminLog`. **Баг-фикс:** `_set_deal` дважды звал `cb_deal_card`→2-й `cb.answer()`→исключение, карточка не перерисовывалась (БД-мутация проходила); фикс — `_render_deal_card(message,did)` без answer. **E2E** `tests/e2e/flow_admin.py` 12/12 + won/reset с аудит-логом. **Урок (шапка flow_admin.py):** меню edit-in-place → пинить `data-mid` пузыря /admin и кликать внутри (broadcast засоряет `.last`); пауза после edit перед кликом (иначе клик в заменённый DOM=флейк). Тест-аккаунт 8688355181 в `TORGIRADAR_ADMIN_IDS` на mini (для e2e; владелец уже админ по OWNER_ID).
**D14 — УПРАВЛЕНИЕ ПОЛЬЗОВАТЕЛЯМИ + ПЕРИОД-СТАТИСТИКА + ПРИВЕТСТВЕННЫЙ БОНУС (проверено e2e, 2026-06-05):** (1) **👥 Пользователи** в админке кнопками: пагин-список `adm:users` (8/стр ◀️▶️) → карточка `adm:user:{tg_id}` (id/username/статус/бонусы/брони/сделки) → [🎁 +бонусная бронь / 🚫Деактивировать↔✅Активировать]; мутации в `AdminLog` (`user_bonus`/`user_toggle`). Без слэш-команд. (2) **📊 Статистика — период-селектор** [Сегодня/7д/30д/Всё] (как zemlya, заменила all-time): за период новые подписчики/лоты/алерты/оплач.брони/выручка/выигранные сделки+fee + блок «всего». `_period_start`+`since(col)` (`sqlalchemy.true()` для Всё). (3) **🎁 Приветственный бонус = 1 бесплатная БРОНЬ:** `Subscriber.free_bookings` default 1, миграция `db.py::_ensure_columns` (идемпотентный `ALTER...ADD COLUMN...DEFAULT 1`, backfill существующих). Кнопка брони в карточке стала нейтральной «📌 Забронировать» (broadcast без юзер-контекста); на экране брони при `free_bookings>0` → «🎁 Забронировать бесплатно» (`payfree:`) вместо оплаты; `cb_payfree` атомарно списывает бонус + `mark_paid(amount=0)` + раскрытие. **E2E** `tests/e2e/flow_users_bonus.py`: период-статистика (разные числа по периодам ✓), users list/card/bonus 1→2 ✓, **бонус-бронь полный путь green** (free-кнопка→«✅ Лот ваш…Серпухов»). Деактивация доказана БД+аудит-логом (дисплей-чтение в hammer-тесте ловит троттлинг правок TG — не баг). **Техника e2e:** читать/кликать ВСЕГДА последний входящий пузырь (история засоряет поиск); бронь итерировать по /lots-карточкам С КОНЦА (свежие, пропуская выкупленные). Тест-данные вычищены. **(4) D14.1 — user-бот на reply-меню** (юзер подтвердил): `handlers.main_menu_kb()` ReplyKeyboardMarkup persistent [🔥 Горячие лоты]/[🎁 Мои бонусы][ℹ️ Как это работает]; хендлеры `F.text.lower().contains(...)` — ⚠️ НЕ `F.text.regexp` (это `re.match`, якорь в начале → эмодзи-префикс ломает матч); /start,/lots сохранены, `_send_lots` общий core. E2E `tests/e2e/flow_menu.py` 4/4. В web.telegram.org reply-клава свёрнута за иконкой (особенность web-клиента, на телефоне видна сразу) — проверял функционально (тап=отправка текста метки). **Кнопка ⚙️ Админка в reply-меню — только админам** (`main_menu_kb(is_admin)`/`_menu(uid)`=`uid in ADMIN_IDS`; хендлер `F.text.lower().contains("админка")` на gated admin_router; в admin.py также `msg_admin_button`). Юзер «не вижу админку» был фикс обнаружимости (config верный, `/admin` и так работал). ⚠️ Чтобы увидеть новую reply-кнопку, надо отправить /start заново (клавиатура обновляется только с новым сообщением).
**D15 — RECON МУЛЬТИ-ИСТОЧНИКОВ (2026-06-05, решено: пока остаёмся на ZK):** «вторая площадка» нашлась В ТОМ ЖЕ torgi — `biddType=229FZ` «Реализация имущества должников» (приставы/distressed): **7641 активных, тот же открытый API**, antibot нет, дисконт-тезис высокий, НО земля ~2–5% (доминируют помещения/здания/авто); кадастр/цена как у ZK (из detail). `67FAS_147FAS` (гос/мун) 3451 но земли ~0. РАД lot-online.ru — 200, antibot НЕТ, но данные за JS/XHR (нужен Playwright-снифф API, средний труд). Федресурс bankrot.fedresurs.ru — **QRATOR 401/403** (только живой браузер, дорого, отложено). Юзер: **пока ничего не подключаем**. Когда вернёмся — дешевле всего **229FZ-земля**: добавить `Lot.source`, обобщить `torgi_id`→`(source,external_id)`, дедуп по `cadastral_number` (слои оценки/брони/рассылки уже source-agnostic). **⚠️ Мини сменил IP .107→.109 (DHCP) — ssh через mDNS `cebers_mini@Cebers-mini.local`** (надёжнее, чем хардкод IP).
**D16 — WEB-РАЗДЕЛ ЛОТОВ (дизайн, 2026-06-09, код не начат; полн. → Obsidian `Web-раздел дизайн.md` + Decisions D16):** делаем lotbot разделом сайта **в ЕДИНОМ бренде «Земля без риска»** (zbrcheck.ru `/lots`, НЕ со-бренд — юзер передумал), доступ к ленте **только после входа** (лендинг+SEO-тизеры публичны), риски = **бейджи на карточке + полное PDF-досье после брони**. Моат vs агрегаторов (TBankrot/Bankrot-Spy и пр., у них уже есть карта/оценка/обременения/менеджер): специализация на ЗЕМЛЕ + прозрачная оценка (дисконт/аналоги/confidence) + land-риски движком zbrcheck (границы/ЗОУИТ/ПЗЗ/соседство/POI) + success-fee. **Ключевое инженерное:** единая личность на zemlya `users.id` (`account_id` в Subscriber/Booking/Deal, резолв telegram_id→users.id, связка `/link_web`, бонус на аккаунт); гонка брони web+бот → **partial unique index SQLite** `uq_active_lock(lot_id) WHERE status='locked'` + `uq_paid_lot`; web-backend к Ext-БД через bash-wrapper (FDA); анонимизация (карта до брони = центроиды). Переиспользуем весь zemlya-web (Next.js/FastAPI/OAuth/Robokassa/карта/AI-PDF/движок). Фазы: MVP(gated-лента+карточка+бронь+бонус+кабинет+единая личность)→Ф2(карта/алерты/досье)→Ф3(deal-score/SEO/Pro/229FZ). **Блокер web-брони:** Robokassa на сайте ЗБР пока ТЕСТ → бронь на сайте ждёт боевого режима ИЛИ временно «забронировать в боте».
**Дальше:** [решить: начинать ли MVP web-раздела D16] → протоколы torgi (success fee verification) → Russian CA для torgi (убрать verify=False) → реальная Robokassa (нужно юрлицо) → slug'и республик → нейтральный прод-бот. Mini: `ssh -i ~/.ssh/id_ed25519 cebers_mini@Cebers-mini.local` (mDNS; IP .109 на 2026-06-05, был .107).

**Мониторинг torgi.gov.ru + дисклеймер брони (2026-06-20, [[torgi_gov_gost_tls]]):** `source_status.py` (probe up/maintenance/unreachable) + singleton-таблица `source_status` + `torgi_monitor.py` launchd `local.torgiradar-torgi-monitor` (300с, алерт ТОЛЬКО админам на смене). `GET /api/source-status`. Сайт: `SourceStatusBanner` (индикатор/баннер). Бот: кнопка «🛰️ Статус torgi.gov.ru» + префикс в меню лотов. Бронь гейтится при down: backend `/api/book` без `ack_source_down`→`source_down`, сайт→модалка, бот→дисклеймер «Понимаю, всё равно бронирую». **🐛 попутно:** фронт `lib/api.ts` бил мимо backend (`/api/bookings*`→404 в проде, Caddy минует dev-rewrite) — бронь с сайта не работала; пути исправлены (`/api/book`,`/api/my-bookings`,`/api/booking/{id}/pay-url|pro`). Алерты админу приходят только если он /start-нул бота (owner ещё не стартовал).

**Pro → ПОЛНЫЙ AI-отчёт + банкротство (ЕФРСБ), 2026-06-20 — полн. → Obsidian Changelog/Decisions D-ai-2026-06-20:** бонус к брони теперь = полный AI-отчёт (ЗОУИТ/эко/инфра/спутник/аналоги/чек-лист), не pricing-Pro. Мост `/internal/lot-pro-report` шлёт `mode='ai'` (аддитивно, дефолт 'pro'); get отдаёт `ai_ready/ai_report_id/ai_content`; `/internal/ai-file` локальный fallback (служебные отчёты без tg_file_id). **Изоляция zemlya-bot:** сервис-юзер uid 178 (tg -777001) + is_paid=0 ⇒ баланс/refund/сообщения НЕ затрагиваются (проверено). Бот `_deliver_pro` двухфазно (TL;DR оценки → PDF полного отчёта); сайт `AiReportPanel` (оценка + iframe `/api/booking/{id}/ai-html` + «Скачать PDF» `/api/booking/{id}/ai-pdf`). **Банкротство условно:** `extract_inn_from_lot` (regex ИНН в description/attributes) → если есть → `/internal/seller-check`+`/seller-check/apply-ai` (ЕФРСБ-конвейер zemlya) → секция «Проверка продавца на банкротство». На текущих лотах ИНН ~нет (0/300) → срабатывает редко. Убрана надпись «бесплатный Pro-анализ» везде (сайт+бот), Pro→AI-отчёт. **🐛 фикс:** бонус-бронь не сгорает при mark_paid=None. **E2E:** бот бронь #7→report 580 ai→completed→PDF+ЗОУИТ; ЕФРСБ-секция в HTML (тест-ИНН). **git init обоих репо на mini (раньше были без VCS):** torgiradar `940b1f4`, torgiradar-frontend `40a4d6f` (ветка main, .gitignore исключает .env/data/logs/node_modules/.bak). Аудит (workflow): зрелость 4/5; НЕ чинились (флагнуто): N+1 в ленте/фасетах, JWT dev-fallback (секрет в проде задан), legacy `/bookings*` без auth (за Caddy), nonce in-memory, `/pay/verify` Password#1.

**РЕБРЕНД «Торги Радар» → «Торги без риска» + ценник (2026-06-20, Obsidian Changelog/Decisions D-rename):** имя «Торги Радар» занято → новый бренд **«Торги без риска»** (семья родительского «Земля без риска», вордмарк «Торги»+«без риска», байлайн «проект «Земля без риска»»). **Пока переименован ТОЛЬКО пользовательский бренд** (сайт layout/manifest/oferta/InstallPrompt/Logo, бот /start, login-email, web-login confirm, админ-панель). НЕ тронуты (отдельным проходом «потом», по слову владельца): пакет/repo `torgiradar`, порты, launchd, поддомен `torgiradar.zbrcheck.ru`, бот-username `@TorgiRadarZBR_bot` (смена тяжёлая: DNS/Caddy/rathole/OAuth/BotFather). Внутренние docstring/комментарии «ТоргиРадар» = код-нейм, оставлены. **🐛 ценник:** в LotCard «Оценка по рынку» больше НЕ зачёркнута (была line-through «старая цена») — обычный muted-текст, скидку показывает бейдж −N%. Коммиты: бот `c1a3238`, фронт `491f08c`. Проверено: build+оба сервиса 200/polling, скрин карточки (без зачёркивания)+мобайла (вордмарк), бот /start «📡 Торги без риска…» через реальный TG.

**НОВЫЙ БОТ @TorgiBezRiska_bot (2026-06-20):** владелец дал токен нового бота «Торги без риска» (id 8675604168, @TorgiBezRiska_bot). Свопнут `TORGIRADAR_BOT_TOKEN` + `TORGIRADAR_BOT_USERNAME` в `.env` (вне git, бэкап `.bak-bottoken-*`/`.bak-botuser-*`), `web/auth.py` fallback и frontend login default → `TorgiBezRiska_bot`. Старый @TorgiRadarZBR_bot больше НЕ опрашивается (token отвязан в коде; в BotFather не трогали). Рестарт бот+backend+frontend; runtime config.BOT_USERNAME=TorgiBezRiska_bot подтверждён; /start нового бота через реальный TG → «📡 Торги без риска…». Коммиты: бот `90aa276`, фронт `bbc716a`.

**ПОДДОМЕН ПЕРЕЕХАЛ: torgiradar.zbrcheck.ru → lots.zbrcheck.ru (2026-06-20).** Владелец сменил DNS в reg.ru (A `lots.zbrcheck.ru → 193.108.113.35`; старый `torgiradar` убран → не резолвится, сайт там лежал). Сделано: VPS `/etc/caddy/Caddyfile` vhost `torgiradar.zbrcheck.ru {`→`lots.zbrcheck.ru {` (backup `.bak-lots-*`, `caddy validate`+`systemctl reload caddy`, TLS перевыпущен Let's Encrypt CN=lots.zbrcheck.ru); backend `.env TORGIRADAR_WEB_BASE_URL` + `config.py` default → `https://lots.zbrcheck.ru` (бэкап `.bak-lotsurl-*`); рестарт backend+бот. rathole-туннели (`torgiradar-frontend`/`torgiradar-api`→18151/18152) и имена НЕ менялись. **Проверено снаружи:** https://lots.zbrcheck.ru /→200, /login→200, /api/regions→509, TLS валиден. Коммит config.py `dd92098`. **Осталось от владельца:** (1) BotFather `/setdomain lots.zbrcheck.ru` для @TorgiBezRiska_bot (one-click TG-виджет; иначе fallback-deeplink); (2) `/start` нового бота владельцем (админ-алерты); (3) OAuth redirect-URI Яндекс/Mail на lots.zbrcheck.ru — позже.

**СЕССИЯ 2026-06-22 (Obsidian Changelog/Decisions — детали):** (1) **Админ-дашборд** `/api/admin/stats` расширен (выручка/конверсия/риск-распределение/топ-регионы) + `/api/admin/users`; фронт `/admin` — вкладки Аналитика/Брони/Пользователи, ссылка «Админка» в шапке для is_admin. Фикс «Лотов в витрине» (был COUNT всей таблицы → теперь размер ленты `_feed_rows`; +`lots_total`). Удалены тест-юзеры; тех-баг dual-DB развеян (живая БД = **проектная**, plist жёстко задаёт `TORGIRADAR_DB_PATH`; Ext не используется живым процессом — CLI-скрипты гнать с тем же env). (2) **Проверка продавца в AI-отчёте +ЕГРЮЛ/ЕГРИП** (`bot/services/egrul.py` в zemlya-bot, egrul.nalog.ru без логина/капчи, failsafe) рядом с ЕФРСБ; +ОКС→ДКП предупреждение в чек-лист. Принцип (Decisions D-due-diligence): автоматизируем только free/без-логина/без-капчи источники; ФССП/kad.arbitr/история участка — бэклог. zemlya-bot commit `2f755ea`. (3) **🔐 ТЕСТ-РЕЖИМ logpass** (`TORGIRADAR_LOGPASS_ONLY=1` в gitignored `.env`, обратимо): вход только логин/пароль (TG/email → 403), брутфорс 3→бан 5ч (разбан в админке), `LotUser.login/password_hash(pbkdf2)/is_active/is_admin/failed_logins/locked_until`. Сид: **Admin** (пароль в `.env`, в чате светился — сменить после теста) + **tester1…tester5** (free_bookings=10). Админка: отключить/включить/разбанить юзеров. backend `db73124`, фронт `7975c76`. **Вернуть TG/email:** убрать `TORGIRADAR_LOGPASS_ONLY` из .env → рестарт API+фронт.

**БОГАТАЯ АДМИНКА (2026-06-23, Obsidian Changelog):** была бедная (3 вкладки) → расширена по образцу zbrcheck (адаптировано: без Pro/AI-тарифов/лидов/выплат). Backend api.py: `AdminLog`-аудит на КАЖДУЮ мутацию (`_admin_log`); `Lot.hidden` (миграция + фильтр в общем `_feed_rows` → скрывает из feed/regions/facets/market/stats + гард в api_book); users с поиском/фильтром(status active|inactive|locked|admin, provider)/пагинацией + `GET /api/admin/users/{uid}` карточка + `set-free-bookings`/`set-admin`(только OWNER_ID)/`delete`(safe, без оплаченных броней); bookings фильтр/пагинация + `/{bid}/expire`+`/{bid}/confirm`(ручное подтверждение→confirm_paid_and_ai_report); `GET/POST /api/admin/lots`+`/{id}/hide`; `GET /api/admin/referrals` лидерборд; `GET /api/admin/audit`; `POST /api/admin/pipeline/run` (launchctl kickstart). Frontend app/admin/page.tsx переписан: 6 вкладок (Аналитика/Пользователи/Брони/Лоты/Рефералы/Аудит), модалка юзера с полным управлением, действия по броням/лотам, тумблер платежей. Проверено: smoke всех эндпоинтов + Playwright-скрины 6 вкладок (console-errors=0). Коммиты ЛОКАЛЬНО (НЕ запушены): backend `c2eb047`, frontend `0605afa`.

**РЕФ-ПРОГРАММА (САЙТ) + СЧЁТЧИК БРОНЕЙ + КАРТА ПО УМОЛЧАНИЮ (2026-06-23, Obsidian Changelog/Decisions D-REF):** Реф-система портирована из zbrcheck по МЕХАНИКЕ (код `REF<lot_user_id>`, capture-cookie `tr_pending_ref`→activate-on-signup, антифрод self/unique/«свежесть»=нет платных броней), но НАГРАДА = бесплатные брони (у lot_users нет денег): каждые **3 приглашённых, оплативших деньгами** → инвайтеру **+1 бесплатная бронь** (повторяемо, идемпотентно). Денежный слой zbrcheck (выплаты/PII-Fernet/договоры/юр-статус) сознательно НЕ портирован. Backend: `LotReferral` (lot_referrals, invited_lot_user_id UNIQUE) + `LotUser.referral_rewards_granted`; `web/referrals.py` (router `/api/referrals`: capture/activate-pending/summary/my-inviter); награда триггерится в `ai_report.confirm_paid_and_ai_report` (ЕДИНАЯ точка «оплачено деньгами» — поллер+/pay/verify; бесплатное раскрытие за free_bookings туда НЕ заходит → не считается). config: `REFERRAL_PER_REWARD=3`, `REFERRAL_REWARD_BOOKINGS=1`. Frontend: `ReferralPanel` (счётчик «🎟 Бесплатные брони: N» + прогресс X/3 + реф-ссылка) на главной (только авторизованным) и в кабинете; `app/r/[code]` приземление; `LotCard` кнопка «🎟 с баланса» (free>0) / «📌 за 2 990 ₽» (→Robokassa, услуга «Бронь земельного лота»); `FilterBar` карта поиска по близости открыта по умолчанию. Онбординг = 1 бронь всем при создании lot_user (как было), приглашённому доп-бонуса нет. **TG-инвайт ВКЛЮЧЁН (2026-06-23, commit `cea0603`):** `tg_invite_link_for`→`t.me/{BOT_USERNAME}?start=ref_{lot_user_id}` (ключ lot_users.id, telegram_id инвайтера НЕ нужен — с ботом работает приглашённый); бот `bot/web_auth.py` ловит `/start ref_<id>` (раньше generic /start) → `upsert_telegram_user`+`try_activate` (гейты self/fresh/unique) + приветствие «Вас пригласил друг»; награда — по той же `confirm_paid_and_ai_report` при оплате брони (бот-оплата test-stub → реально на сайте). Фронт не менялся (`ReferralPanel` уже читал `tg_invite_link`). Проверено: движок-тест на боевой БД + живой TG `/start ref_3`→`LotReferral(3→11)` + Playwright-скрин кнопки. Проверено: engine-тест (3 оплаты→+1, идемпотентность, сброс прогресса, self-ref reject), HTTP E2E по HTTPS с куками, Playwright-скрины авторизованной главной (панель+«с баланса»+карта открыта, console-errors=0)+`/r/REF`. Коммиты ЛОКАЛЬНО (НЕ запушены): backend `8d3cb95`, frontend `e0610d0`. **Открыто:** welcome-бонус всем на проде — оставить 1 или обнулить (на усмотрение перед запуском); bot-side рефералка по subscribers.

**РИСК-СКРИН ЛОТА (2026-06-21, Obsidian Changelog):** «сразу проверять лоты» → новый `torgiradar/risk_screen.py`: быстрый HTTP point-in-zone по 18 курированным риск-слоям НСПД (без браузера ~0.3с), уровни blocked🔴/caution🟡/clean🟢/unknown⚪. Зовётся в `compute_valuation` (переиспускает координаты, +0.3с); `Valuation.risk_level/risk_note` (+миграция). Лента отдаёт риск, 🔴 дерангируется вниз (не скрыт); карточка — светофор+причина. **Точную проверку ЗОУИТ (браузерную analyze_land_constraints, в т.ч. водоохранную/СЗЗ/ООПТ) НЕ дублируем — она в AI-отчёте после брони.** Исключены вездесущие не-риски (875838 «Территориальные зоны» и пр.). Коммиты бот `08028d9`, фронт `58d4058`. Backfill текущих лотов — фоном.
