📄

Брендирование электронных полисов ОСАГО в Telegram

для аварийных комиссаров: контакты на бланке, данные и QR-код не тронуты

Telegram-бот наносит контакты аварийного комиссара на электронный полис ОСАГО за пару секунд, не задевая данные, QR-код и блок подписи. Бланки четырёх страховых. Python, PyMuPDF, aiogram 3.

В продакшенеNDA
4 страховых
поддержаны бланки четырёх компаний, страховщик определяется по тексту страницы
380 → 520 КБ
вес полиса после брендирования, а не 6,5 МБ, как было в первой сборке
0 файлов
хранится на диске: документ живёт только в памяти, пишутся лишь счётчики

Задача

Аварийные комиссары работают по звонку: клиент попал в ДТП, достаёт полис ОСАГО и звонит. Раньше фирма клеила на распечатанный бланк наклейку со своим телефоном и ставила печать. Электронный полис так не заклеишь, а клиент чаще всего хранит его в телефоне.

Нужно получить тот же PDF, но с контактами комиссара на нужных местах: за пару секунд и без вёрстки руками. Объём небольшой, до 20 бланков в день, работают три человека. Главное требование — не повредить сам документ: номер бланка, данные владельца и автомобиля, QR-код и подпись страховщика должны остаться нетронутыми.

Решение

Telegram-бот принимает готовый электронный полис ОСАГО и возвращает его же с брендированием: жёлтая карточка с телефоном комиссара вместо служебного блока страховщика и печать «ЗВОНИТЬ ПРИ ДТП» поверх абзаца с условиями. Что реализовано:

  • защищённые зоны: номер бланка, ФИО, VIN, госномер, премия, QR-код и блок электронной подписи, а также логотип и горячая линия страховщика остаются на месте;
  • автоматический поиск места: карточка и печать ищутся по тексту страницы, а не по заданным координатам;
  • распознавание бланков четырёх страховых компаний, для одной из них включается своя стратегия размещения;
  • оптимизация веса: полис на 380 КБ выходит примерно в 520 КБ;
  • обработка без хранения: документ живёт в оперативной памяти и сразу уходит обратно;
  • доступ по белому списку и по кнопке владельца, статистика обработок за день, неделю, месяц, год и по людям;
  • меню на кнопках, полоска прогресса по этапам обработки и скрытая команда /version для владельца;
  • командная строка inspect, check, brand для проверки раскладки на своём бланке без бота.

Как это устроено

Защищённые зоны PDF: что нельзя закрывать

Первая версия ставила блоки по координатам из настроек и «развалилась» уже на втором бланке. Сейчас бот сначала разбирает страницу: блоки, строки и слова текста, картинки, QR-код. Всё, что похоже на данные полиса, попадает в список запретных прямоугольников. Затем карточка ужимается, пока не перестанет пересекаться с ними: обрезается та сторона, которой она заходит меньше. Ширина карточки ограничена 45 % страницы, чтобы она не растягивалась на весь лист.

Поиск блока по тексту в PDF

Место для карточки бот находит по смыслу: на любом бланке есть абзац об уведомлении финансового уполномоченного, который ничего не значит для клиента. Он ищется регулярным выражением, и его прямоугольник становится местом карточки. Если абзаца нет, берётся шапка страховщика. Печать ложится поверх абзаца с пунктами 4–5, который тоже ищется по началу и концу.

Карта занятости: где на странице свободно

Если нужный абзац не нашёлся, свободное место подбирается по карте занятости. Страница нарезается на клетки по 1,5 пункта, по ним строится таблица сумм, и любой прямоугольник проверяется на пустоту за одно действие. Поиск идёт сначала правее, потом ниже.

Размер PDF после наложения

Стирание текста под карточкой по умолчанию перерисовывает и картинки. На реальном бланке под текстом лежит растровая подложка во всю страницу, и файл пересжимался с 380 КБ до 6,5 МБ. Решение — стирать только текст, а картинки не трогать: визуально ничего не меняется, карточка и так непрозрачная. Шрифты подрезаются до использованных символов, иначе каждый добавлял бы по 700 КБ. На это в проекте есть тест.

Текст карточки по приоритету

Текст карточки собирается группами с приоритетом. Если места мало, первыми уходят телефон страховщика, сайт, перечень услуг и запасной номер, а главный телефон и название остаются всегда. Размер шрифта подгоняется под высоту блока.

Машинная приёмка результата

Команда check — автоматическая проверка результата: она сообщает, если карточка вылезла за свой блок, печать вышла за абзац 4–5 или задета защищённая зона. Тяжёлая работа уходит в отдельный поток, чтобы не блокировать приём сообщений от Telegram.

Результат

  • Полис с контактами комиссара готов за пару секунд после отправки файла, без ручной вёрстки.
  • Номер бланка, данные владельца и автомобиля, премия и QR-код не задеваются.
  • Размер файла после брендирования сопоставим с исходным, порядка +40 %, а не в разы.
  • Документы клиентов нигде не сохраняются: на диске остаются только счётчики.
  • Первый выпуск сопровождался 42 тестами на синтетическом бланке; на реальных бланках автоматической проверки нет.

Технологии: что и зачем

  • Python 3.13 — язык сервиса.
  • PyMuPDF — разбор страницы PDF и перерисовка: блоки текста, картинки, стирание и наложение.
  • aiogram 3 — Telegram-бот: меню, прогресс обработки, статистика, доступы.
  • segno — QR-код в карточке.
  • SQLite — счётчики обработок без файлов и их содержимого.
  • Docker Compose — запуск: контейнер только для чтения, без открытых портов, бот сам обращается к Telegram.

Статус

Версия 1.1.1 от 20 сентября 2026 года, в продакшене у заказчика. Известные ограничения: после брендирования встроенная подпись страховщика не проверяется (подлинность подтверждается по QR и на сайте РСА); поддержаны бланки только четырёх страховых компаний; нельзя запускать два экземпляра с одним токеном бота; резервная копия тома со счётчиками делается вручную. Другие детали заказчика не раскрываются.

Вопросы по проекту

Как добавить контакты аварийного комиссара на электронный полис ОСАГО?
Сотрудник присылает боту PDF полиса и через несколько секунд получает тот же файл с жёлтой карточкой контактов и печатью «ЗВОНИТЬ ПРИ ДТП». Карточка встаёт на блок бланка, который не нужен клиенту, а печать ложится поверх абзаца с условиями.
Не закроет ли брендирование номер бланка, QR-код или данные полиса?
Нет. Бот разбирает страницу и объявляет защищёнными зонами номер бланка, ФИО, VIN, госномер, премию, QR-код и блок электронной подписи. Элементы брендирования в эти области не заходят. Отдельная команда проверки сообщает, если что-то задето.
Что происходит со встроенной электронной подписью страховщика?
После изменения файла подпись перестаёт проверяться. Полис при этом действует, а подлинность проверяется по QR-коду и на сайте РСА. Бот предупреждает об этом один раз и закрепляет сообщение в чате.
Почему файл после наложения не раздувается в несколько раз?
Бот стирает только текст под карточкой и не трогает картинки, а шрифты оставляет только с использованными символами. Полис на 380 КБ выходит примерно в 520 КБ, а не в 6,5 МБ.
Где хранятся полисы клиентов?
Нигде. Файл обрабатывается в оперативной памяти и сразу отправляется обратно. На диск пишутся только счётчики: когда, кто, сколько страниц и байт и за сколько миллисекунд. Ни файлов, ни их имён, ни данных из полиса.
Как бот понимает, чей это бланк, и работает ли он с незнакомыми страховщиками?
Страховщик определяется по тексту страницы, поддержаны бланки четырёх страховых компаний. Для других работает общий разбор страницы, но без гарантий: координаты из настроек служат лишь запасным вариантом.

Нужно похожее?

Расскажите о задаче — покажем, как решали такое, и прикинем объём работ.