Skip to content

Repository files navigation

Заявки в УК через MAX

Мини-приложение и бот в 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"

Основной сценарий

  1. Житель открывает бота по ссылке или QR-коду с подъезда, дом подставляется сам.
  2. В мини-приложении выбирает, где проблема (в доме, во дворе, в городе) и что случилось, указывает подъезд, место, описание и по желанию фото. Если похожая заявка уже есть, приложение предлагает поддержать её вместо новой.
  3. Заявка уходит ответственной организации автоматически: категория знает, кто отвечает (УК, АДС, РСО, муниципальная служба, участковый) и какой срок по нормативу. Бот присылает в чат: «Заявка № N зарегистрирована. Ответственный: АДС УК. Реакция по нормативу: до 16.09 12:30. Устранение: до 19.09 12:00. Основание: ПП РФ № 416, п. 13».
  4. Организация получает заявку через 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:

  1. Открыть http://localhost:8080/. Приложение представится тестовым жителем, экран «Мои заявки» пуст, внизу вкладки «Заявки», «Дом», «Профиль».
  2. Нажать «Новая заявка», выбрать категорию, указать место, отправить. Дом берётся из профиля, где его можно сменить; ссылка с кодом дома выбирает его сама.
  3. Откроется карточка: статус «Отправлена, ждём регистрации» сменится на «Зарегистрирована», появятся ответственный, сроки, основание и строка в истории.
  4. В списке заявка показана с номером и сроком устранения. Без токена бот только регистрирует заявку, сообщение в чат не отправляется.
  5. Вкладка «Дом» показывает заявки всех жителей этого дома с поиском по категории, месту и тексту, а на вкладке «Новости» отключения и объявления. В карточке заявки кнопка «Тоже беспокоит» добавляет голос соседа.
  6. Открыть приложение как админ: по ссылке-приглашению с STAFF_SECRET, локально #!start=staff_<секрет>, либо #!user=7 при UK_STAFF=7. Раздел «Диспетчер» показывает последние открытые заявки со сменой статуса, кнопку «Все заявки», статистику, QR дома и форму новости. Раздел «Админ»: новый дом, сотрудники с домами, интеграции организаций. Коллегу можно добавить по коду сотрудника из его профиля, доверие идёт цепочкой от ключа бота.
  7. 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 смотрит на публичный адрес:

  1. Написать боту /start, нажать «Подать заявку».
  2. Подать заявку, получить сообщение с номером, ответственным и сроками.
  3. Повторная подача даёт следующий номер, перезапуск бота не дублирует сообщения: отметки об отправке хранятся в базе.

Ожидаемое поведение при ошибках: неверная подпись даёт 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.

About

max hackaton!

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages