Мини-приложение и бот в MAX для жителя многоквартирного дома. Житель описывает проблему, а в ответ сразу получает в чат номер заявки, ответственную организацию и срок по нормативу. Смена статуса заявки диспетчером приходит жителю сообщением в MAX.
Хакатон «Умный город», трек управления МКД. Репозиторий: https://github.com/b-on-g/max.
Кто что может и все сценарии по шагам: docs/ROLES.md.
Разбор соответствия заданию глазами жюри: docs/REVIEW.md.
Исследование проблемы, данные, метрики, масштабирование и пилот: docs/RESEARCH.md.
Презентация: docs/presentation.pdf, исходник docs/presentation.html,
PDF собирается командой ниже.
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --headless=new --print-to-pdf=docs/presentation.pdf --no-pdf-header-footer "file://$PWD/docs/presentation.html"- Житель открывает бота по ссылке или QR-коду с подъезда, дом подставляется сам.
- В мини-приложении выбирает, где проблема (в доме, во дворе, в городе) и что случилось, указывает подъезд, место, описание и по желанию фото. Если похожая заявка уже есть, приложение предлагает поддержать её вместо новой.
- Заявка уходит ответственной организации автоматически: категория знает, кто отвечает (УК, АДС, РСО, муниципальная служба, участковый) и какой срок по нормативу. Бот присылает в чат: «Заявка № N зарегистрирована. Ответственный: АДС УК. Реакция по нормативу: до 16.09 12:30. Устранение: до 19.09 12:00. Основание: ПП РФ № 416, п. 13».
- Организация получает заявку через API или диспетчер меняет статус в приложении, житель получает уведомление и видит историю. Соседи видят заявку в разделе «Дом» и могут её поддержать, поддержанные заявки видны организации с числом голосов.
| Компонент | Путь | Роль |
|---|---|---|
| Модель | house/, category/, ticket/, uk/, owner/, status/ |
Схема данных Гипербазы и правила: кто ответственный, какой срок |
| Тестовые данные | seed/ |
Два дома и семь категорий с нормативами |
| Бот | bot/ |
Node-процесс: мастер Гипербазы, POST /auth, уведомления, /start |
| Проверка initData | bot/check/ |
HMAC-SHA256 по алгоритму MAX, тест рядом |
| Мини-приложение | app/ |
Разделы «Заявки», «Дом» (заявки соседей и новости), «Профиль», «Диспетчер» для персонала УК, форма и карточка заявки |
| API организаций | bot/org/ |
GET /org/tickets и POST /org/status по ключу организации, описание в openapi.yaml |
| Новости дома | post/ |
Отключения и объявления УК, публикует диспетчер |
| Навигация | nav/ |
Нижняя панель вкладок, как в мобильных приложениях |
| Мост MAX | bridge/ |
Обёртка над window.WebApp: initData, платформа, ready |
| Тема | theme/ |
Токены дизайн-системы MAX из @maxhub/max-ui, переложенные на переменные $mol |
Стек: $mol и MAM, Гипербаза как
локальная CRDT-база с синхронизацией, @maxhub/max-bot-api
как официальный клиент Bot API MAX.
Данные лежат в одном ленде УК, который создаёт бот при первом запуске. Ключ бота владеет
лендом. Жителю после проверки initData выдаётся право post: он дописывает свои заявки,
а статус читается только от ключей УК. Ленд зашифрован, читать его могут только
привязанные жители и УК.
POST /auth { init_data: WebApp.initData, pass: <публичный ключ жителя> }
200 { land, house | null, user: { id, name } }
401 подпись MAX не прошла проверку или auth_date старше часа
422 тело не JSON или pass не похож на ключ
Токен бота видит только бот, поэтому подпись initData проверяется там. Дальше мини-приложение синхронизирует ленд напрямую с ботом по WebSocket.
Адрес бота приложение берёт так: на GitHub Pages это константа bot_prod в
app/app.view.tree, в локальном Docker свой origin, потому что nginx перед статикой
проксирует /auth, /org, файлы и WebSocket на бота. Для отладки адрес передаётся
параметром #!bot=host:port без схемы и слэшей, $mol_state_arg режет аргументы по /.
cp .env.example .env # вписать BOT_TOKEN
docker compose up --buildМини-приложение: http://localhost:8080/, там же /auth и синхронизация через nginx.
Бот напрямую: http://localhost:9090/.
Остановка docker compose down, повторный запуск той же командой. Данные и ключ бота
живут в томах baza и state, docker compose down -v стирает их вместе с лендом.
| Переменная | Назначение |
|---|---|
BOT_TOKEN |
Токен бота из MAX. Обязателен для уведомлений и проверки подписи |
APP_URL |
Публичный HTTPS-адрес мини-приложения, его открывают кнопки в чате |
BAZA_AUTH |
Приватный ключ бота. Пусто: ключ создаётся при первом запуске и хранится в томе state |
DEV_SKIP_VALIDATION |
1 отключает проверку подписи для проверки без MAX. В проде 0 |
BOT_NAME |
Имя бота для ссылок и QR вида https://max.ru/<имя>?start=house_<код>. Пусто: берётся из Bot API по токену |
STAFF_SECRET |
Секрет приглашения: ссылка https://max.ru/<бот>?start=staff_<секрет> делает открывшего админом. Дальше админ заводит дома и добавляет сотрудников в приложении по коду из профиля |
UK_STAFF |
Запасной путь: ID пользователей MAX через запятую, которым сразу открыт раздел «Диспетчер». Свой ID бот сообщает по команде /id |
ORG_KEYS |
Ключи организаций для API вида uk:ключ,ads:ключ,municipal:ключ |
| Порт | Сервис |
|---|---|
| 8080 | Мини-приложение, nginx |
| 9090 | Бот: POST /auth и WebSocket мастера Гипербазы |
Версии зафиксированы в bot/run/package.json: @maxhub/max-bot-api 0.3.1, jsdom 29.1.1,
autoinstall 0.3.1. Остальное тянет MAM из git по .meta.tree при сборке образа.
Мини-приложение живёт на GitHub Pages: https://b-on-g.github.io/max/, этот адрес и указан
в боте MAX. На VPS работает только бот: поверх базового compose кладётся
docker-compose.prod.yml, который не поднимает nginx, а бота выставляет на
127.0.0.1:8081, и caddy-docker-proxy выпускает сертификат на MAX_DOMAIN по лейблу.
Сейчас это https://cmyser-ru-max.91.188.212.151.ip.giper.dev/, он же прописан в bot_prod
приложения для сборки на Pages.
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build- Bot API MAX: long polling за обновлениями и отправка сообщений. Входящих соединений от MAX не нужно.
- MAX Bridge в мини-приложении:
initData,platform,start_param.
Интеграций с ГИС ЖКХ, платформой обратной связи Госуслуг и системами УК нет, они модельные:
по приказу Минстроя № 856/пр официальные обращения регистрируются в ГИС ЖКХ, а MAX работает
через интеграцию с ней, поэтому в пилоте регистрация заявки дублируется в ГИС ЖКХ. Дома,
категории и нормативы это тестовые данные, см. seed/seed.ts. Нормативы взяты из ПП РФ
№ 416, № 354, № 170, ГОСТ Р 50597-2017 и 59-ФЗ, ссылки в docs/RESEARCH.md, перед пилотом
их сверяет юрист УК.
Без MAX, DEV_SKIP_VALIDATION=1. Параметр #!user=7 в адресе открывает приложение от имени
тестового пользователя с этим ID, так проверяется раздел диспетчера, если ID есть в UK_STAFF:
- Открыть http://localhost:8080/. Приложение представится тестовым жителем, экран «Мои заявки» пуст, внизу вкладки «Заявки», «Дом», «Профиль».
- Нажать «Новая заявка», выбрать категорию, указать место, отправить. Дом берётся из профиля, где его можно сменить; ссылка с кодом дома выбирает его сама.
- Откроется карточка: статус «Отправлена, ждём регистрации» сменится на «Зарегистрирована», появятся ответственный, сроки, основание и строка в истории.
- В списке заявка показана с номером и сроком устранения. Без токена бот только регистрирует заявку, сообщение в чат не отправляется.
- Вкладка «Дом» показывает заявки всех жителей этого дома с поиском по категории, месту и тексту, а на вкладке «Новости» отключения и объявления. В карточке заявки кнопка «Тоже беспокоит» добавляет голос соседа.
- Открыть приложение как админ: по ссылке-приглашению с
STAFF_SECRET, локально#!start=staff_<секрет>, либо#!user=7приUK_STAFF=7. Раздел «Диспетчер» показывает последние открытые заявки со сменой статуса, кнопку «Все заявки», статистику, QR дома и форму новости. Раздел «Админ»: новый дом, сотрудники с домами, интеграции организаций. Коллегу можно добавить по коду сотрудника из его профиля, доверие идёт цепочкой от ключа бота. - API организации:
curl -s http://localhost:9090/org/tickets -H 'Authorization: Bearer <ключ из ORG_KEYS>'
curl -s -X POST http://localhost:9090/org/status -H 'Authorization: Bearer <ключ>' \
-H 'content-type: application/json' -d '{"ticket":"<link>","status":"done","note":"Лифт запущен"}'Ответ содержит заявку с новым статусом, житель получает уведомление в чат.
В MAX, DEV_SKIP_VALIDATION=0, APP_URL смотрит на публичный адрес:
- Написать боту
/start, нажать «Подать заявку». - Подать заявку, получить сообщение с номером, ответственным и сроками.
- Повторная подача даёт следующий номер, перезапуск бота не дублирует сообщения: отметки об отправке хранятся в базе.
Ожидаемое поведение при ошибках: неверная подпись даёт 401 с текстом, приложение показывает его и предлагает открыть заявку заново из MAX.
- Персонал УК задаётся списком ID в
UK_STAFF, роли внутри УК не различаются. - Несколько домов у одного жителя не поддерживаются, дом один, но можно сменить в профиле.
- Все привязанные жители УК видят заявки друг друга. Для дома это скорее плюс, но фото и описания стоит писать без личных данных.
- Экран диспетчера, QR-коды на подъезды и фото к заявке идут после основного сценария.
- Проверка подписи выполнена по открытым реализациям алгоритма MAX, сверить с документацией dev.max.ru перед сдачей.
cd /path/to/mam && npm start
DEV_SKIP_VALIDATION=1 node bog/max/bot/run/-/node.js port=9097
open 'http://localhost:9080/bog/max/app/-/index.html#!bot=localhost:9097'
node bog/max/bot/run/-/node.test.js
node bog/max/app/-/node.test.jsБот пишет данные в .baza текущего каталога и ключ в ~/.local/share/mol_state_local,
для отладки удобно запускать его из отдельной папки с XDG_DATA_HOME.
Скриншоты всех экранов на 1280 и 400 через headless Chrome, нужны дев-сервер и бот:
node bog/max/probe/-/node.js dir=/tmp/max-shots bot=localhost:9097 ticket=<ссылка заявки>Деплой мини-приложения на GitHub Pages идёт из .github/workflows/deploy.yml при пуше в main.