Коммерческие отделы ежедневно тратят много времени на ручное создание однотипных документов: копирование реквизитов из переписки, подстановка ФИО в нужном падеже, расчет графиков платежей, НДС и сумм прописью. Одна пропущенная цифра в ИНН или ошибка в склонении должности, и договор возвращается на доработку, а это задержка сделки.
В этой статье я покажу как собрал на Python Telegram-бота, который превращает 15-30 минут работы в ворде (борьбой с выравниваниями, шрифтами, отступами и пр.) в 5-минутный диалог. Никаких сложных CRM, никаких конструкторов с долгим обучением. Только async, последовательное управление состояниями, регулярные выражения и генерация готовых Word-файлов.
Архитектура решения
-
Интерфейс: Telegram Bot (
python-telegram-botv20+, асинхронный режим) -
Управление состояниями: явное хранение шагов диалога в оперативной памяти с поддержкой возврата на предыдущий вопрос
-
Парсер реквизитов: регулярные выражения + правила для автоматического определения типа субъекта (ИП или ООО)
-
Генератор документа:
docxtpl+ шаблон.docxс динамическими блоками и вставкой внешних приложений -
Запуск: облачный хостинг, секреты передаются через переменные окружения, корректное завершение работы при перезапуске
Ключевые технические решения
Telegram, очевидно, не сохраняет контекст переписки. А мне было необходимо собрать все ответы пользователя за итерацию, чтобы их аккуратно перенести в шаблон документа (предварительно размеченный). Для первой рабочей версии я реализовал явную машину состояний в оперативной памяти: каждый шаг фиксируется в словаре user_data[chat_id], а также реализовал команду возврата (/back), которая откатывает индексы и ответы пользователя. Это нужно для исправления ошибок ввода, которые часто видно сразу. То есть можно откатиться на шаг назад и исправить ответ.
# bot.py (фрагмент)async def handle_back(update: Update, context: ContextTypes.DEFAULT_TYPE): chat_id = update.effective_chat.id state = user_data.get(chat_id) if not state: return step = state.get("step") if step == "main_flow": f_idx = state.get("flow_idx", 0) if f_idx > 0: # Удаляем текущий ответ, откатываем индекс curr_key = FLOW_STEPS[f_idx] state.pop(curr_key, None) state["flow_idx"] = f_idx - 1 # Пропускаем лишние шаги, если они были скрыты логикой prev_key = FLOW_STEPS[state["flow_idx"]] if prev_key == "pay_date_3" and state.get("payment_type") == "50/50": state["flow_idx"] -= 1 prev_key = FLOW_STEPS[state["flow_idx"]] await update.message.reply_text(QUESTIONS[prev_key])
Парсинг реквизитов
Менеджеры присылают реквизиты в произвольном виде: кто-то копирует из 1С, кто-то из мессенджера, кто-то копирует со скриншота с распознаванием текста. Парсер вытаскивает ИНН, ОГРН/ОГРНИП, КПП, расчётный и корреспондентский счета, БИК, email и адрес, автоматически определяя статус субъекта.
# requisites_parser.py (фрагмент)def parse_requisites(text: str) -> dict: text = clean(text) data = {"company_name": text.split("\n")[0].strip() if text else ""} full_text = text # ИНН (10 или 12 цифр) m = re.search(r"(?i)ИНН\s*[:\-]?\s*(\d{10,12})", full_text) data["inn"] = m.group(1) if m else "" # ОГРН / ОГРНИП: определяем тип субъекта по длине числа m_ogrn = re.search(r"(?i)ОГРН(?:ИП)?\s*[:\-]?\s*(\d{13,15})", full_text) if m_ogrn: ogrn_val = m_ogrn.group(1) data["ogrn"] = ogrn_val data["is_ip"] = len(ogrn_val) == 15 # 15 цифр = ИП, 13 = ООО else: # Резервный вариант: ищем "ИП" в названии компании data["is_ip"] = "ИП " in data["company_name"].upper() # ... дальнейший парсинг КПП, счетов, БИК, Email, Адреса return data
Сейчас пользователю не нужно заполнять отдельные поля. Достаточно отправить текст с реквизитами в чат. Регулярные выражения покрывают ~95% случаев копипасты из различных источников.
Динамический расчёт платежей и НДС
В договорах часто меняются схемы оплаты и порядок оплат, ставки НДС и суммы. Логика полностью вынесена в Python, что исключает ошибки при ручном пересчёте в таблицах. Суммы прописью генерируются через num2words, НДС считается корректно в общей сумме контракта в зависимости от выбранной системы налогообложения.
Русская грамматика в преамбуле
Юридически корректная формулировка требует родительного падежа: в лице Генерального директора Иванова И.И.. Полноценная языковая модель здесь избыточна, поэтому я применил ручной подход: правила склонения + словари несклоняемых исключений.
def decline_fio(fio: str) -> str: parts = fio.strip().split() if len(parts) != 3: return fio surname, name, patronymic = parts is_female = patronymic.endswith(('вна', 'шна')) # надёжный маркер пола # Склонение фамилии по правилам + исключения if surname.endswith(("ова", "ева", "ина")): surname = surname[:-1] + "ой" elif not is_female and surname[-1] not in "аеёиоуыэюя": surname += "а" # ... аналогично для имени и отчества return f"{surname} {name} {patronymic}"
Это покрывает ~90% кейсов. Для нестандартных имен оставлены резервные варианты, да и ручная правка текста в готовом файле остается рабочим вариантом.
Результаты
-
Время создания документа:
~5 мин диалогаvs15–30 минут ручного заполнения -
Ошибки в реквизитах и расчетах сведены к минимуму благодаря автоматической проверке и формулам
-
Бот размещен в облаке, потребление памяти
<128MB, холодный старт<3 сек -
Бот работает в привычном мессенджере, не требует установки дополнительного ПО, доступен с телефона
Планы на развитие
-
Переход на вебхук + базу данных для хранения истории сессий и аудита изменений.
-
Проверка ИНН/ОГРН через открытые интерфейсы ФНС для мгновенной верификации контрагентов.
-
Поддержка экспорта в форматы электронного документооборота.
-
Поддержка разных валют и многоязычных шаблонов для внешних контрактов.
-
Публикация исходного кода после финальной чистки конфигураций.
Заключение
Автоматизация юридических и коммерческих процессов не обязательно требует сложных корпоративных конструкторов. Связка Telegram + асинхронный Python + регулярные выражения + docxtpl дала мне быстрый, надежный и недорогой инструмент, которым менеджеры начинают пользоваться в первый же день. Код легко адаптируется под любой шаблонный договор: достаточно обновить .docx и скорректировать маппинг полей в логике диалога.
ссылка на оригинал статьи https://habr.com/ru/articles/1036960/