Как я построил систему тишины для 13 ботов — и ещё 7 задач за одну неделю

В 5 утра 2 мая Тригуна, мой балийский менеджер вилл, проснулся от звука уведомления в Telegram. Не одного. Первым пришло уведомление от системы управления бронированиями — отчёт про незавершённый чек-аут на вилле №7. Человек, который должен был выехать вчера в 11 утра, до сих пор в номере. Это нужно решать. Ночью Тригуна не читает чат, но звук будит.

Через два часа, в три ночи местного времени, пришло ещё одно уведомление: «Напоминание #6» — напоминание о том что завтра нужно проверить уборку. Пришло не от человека, пришло от автоматизированной системы. Опять звук. Опять будит.

В три ночи третий бот рапортовал что миграция данных апарт-отеля на новую управляющую компанию прошла успешно, прислал полный лог из 14 строк с информацией что именно синхронизировалось. Это хорошая новость, но её можно дождаться и днём.

Балийцы спят с 21 до 06 часов вечера на местном времени. Это не ночь на которую иногда просыпаются предприниматели в России во время кризиса. Это основной сон команды. Ежедневный восьмичасовой сон. Никто из моей балийской команды не читает деловые чаты в реальном времени между 21 и 09 утра. Это их личное время. Но звуки в телефоне всё равно будят. И это повторялось каждую ночь. Каждую. 10 уведомлений в среднем за ночь. Это была проблема, которую я нужно было решать в эту неделю, и я решил её в понедельник до полудня.

Проблема: 13 источников уведомлений без координации

Открыл код и пересчитал все точки в системе откуда боты отправляют сообщения в рабочий чат `booking-chat`, где сидит вся операционная команда Бали. У меня их ровно 13. Это не преувеличение. Точно 13 разных скриптов или сервисов которые знают как писать в этот чат.

Система управления бронированиями (EZee PMS) отправляет уведомления каждые 15 минут о статусе всех текущих чек-аутов и чек-инов. Сколько гостей выехали, сколько ещё в номерах, какие есть проблемы. Система уборки отправляет отчеты о завершённых работах раз в час. Система финансов готовит еженедельный отчёт о доходах и расходах. Система мониторинга оборудования в виллах отправляет алерты если кондиционер неправильно показывает температуру или вода горячая перестала идти. Система управления инвесторами отправляет еженедельный digest про прибыль каждого инвестора. Система контроля цен отправляет уведомления если где-то обнаружены ошибки в установленных тарифах. Система обнаружения двойных бронирований. Система проверки соответствия мощности вилл. И ещё семь других систем, каждая со своей логикой и своим расписанием.

Только одна система знала про то что на Бали ночь и люди спят. Это была система финансового дашборда, которая знала про временную зону конкретно потому что я перед её написанием погуглил и добавил параметр `timezone='Asia/Jakarta'`. Остальные 12 систем писали круглосуточно. Четыре часа ночи по местному времени? Неважно. Система управления бронированиями всё равно пошлёт отчёт о том что нет планов чек-аутов в следующие два часа (это полезно днём, когда менеджер планирует день, но ночью это шум).

До полудня среды я считал это нормальным. В конце концов, информация приходит. Бизнес требует информации. Но информация которая не читается вовремя становится шумом. Шум снижает внимание к реальным проблемам. Если в чате 50 уведомлений за ночь, и Тригуна смотрит чат в 09 утра и видит всё сразу, он потратит час на разбор вместо пяти минут. Критичные алерты про реальные проблемы тонут в фоновом шуме от плановых уведомлений которые выполнили свою роль но продолжают занимать место в истории.

Архитектурная проблема была ещё глубже. Каждая система отправляла сообщения со своей логикой, своим форматом, своим расписанием. У меня не было единой точки контроля над всеми 13. Если нужно было изменить что-то глобальное — например отключить уведомления для определённой виллы которую я передаю другой управляющей компании, или сделать сообщения более лаконичными, или добавить ещё одну платформу кроме Telegram — пришлось бы трогать все 13 файлов в разных частях кода. И каждый раз риск что-то забыть в одном из мест, когда-нибудь это вылезет багом.

Решение: архитектура единого модуля с окном тишины

Я создал `/opt/notification_coordinator.py` — единый сборщик уведомлений. Это не сложный файл. 200 строк кода. Но эти 200 строк занимают позицию посередине между всеми 13 источниками и Telegram API.

Все 13 источников теперь импортируют одну функцию вместо того чтобы писать напрямую в Telegram:

from notification_coordinator import send_alert
send_alert(villa_id=7, type='checkout_overdue', message='Гость не выехал')

Функция внутри знает про окно тишины, про очередь сообщений, про форматирование, про исключения. Если сейчас ночь (между 21 и 09 по местному времени в Asia/Jakarta) — сообщение не отправляется сразу в чат. Оно копится в очередь в памяти сервера. В 09 утра по крону запускается функция которая берёт все накопленные за ночь сообщения и выходит пачкой в один дайджест. Если это алерт про виллу которая уже под управлением другой компании — функция проверяет флаг в базе и не отправляет вообще. Если это плановое уведомление которое может подождать — оно ждёт. Если это критичный алерт про проблему в текущей вилле днём (в часы работы) — он идёт сразу, не копится в очередь.

Это не была революция в архитектуре. Я не переписал всё с нуля, не переделал 13 систем. Я создал одну прослойку между шумом и приёмником. Эта прослойка скоординировала 13 отдельных систем. Вместо 13 разных расписаний и 13 разных логик отправки — теперь всё идёт через одно место где одна логика.

После внедрения: ночные уведомления сократились на 100%. Балийцы спят без звуков в телефоне. Информация не теряется — она копится и выходит утром дайджестом. Тригуна может спать спокойно до 06:00, а потом в 09 утра потратить 10 минут чтобы прочитать все ночные отчеты пачкой вместо того чтобы просыпаться каждый час от очередного звука.

Принцип единой правды: одно поле вместо 74 правил

На той же неделе я закрывал переезд двух апарт-отелей на новую управляющую компанию. Это была сложная операция требующая синхронизации. У меня в системе бронирования висели 17 будущих броней — гости уже забронировали номера и рассчитывали на мою команду для управления их проживанием во время их отпуска. Мы договорились что новая управляющая компания отыграет эти бронирования, я пересчитал бухгалтерию по про-рата базису (раздел стоимости каждого дня по соответствующей компании), обновил контракты.

Но алертные системы продолжали заполнять чат как если бы это были мои виллы под моим управлением. Система обнаруживала двойные бронирования — потому что гость забронировал раньше у меня, потом его переводили на новую управляющую компанию, в системе остались обе записи. Система проверяла завершены ли чек-ауты на эти даты. Система мониторила сколько людей должны быть в номере по бронированию. И каждый раз система орала в чат: «ДВОЙНОЕ БРОНИРОВАНИЕ!» или «НЕЗАВЕРШЁННЫЙ ЧЕК-АУТ!» или «НАРУШЕНИЕ МОЩНОСТИ!»

Но это уже не наша проблема. Это проблема новой управляющей компании. Моя система должна была умолкнуть для этих вилл, остановить всю аналитику и мониторинг.

Раньше я бы открыл три файла в коде. В каждом файле нашёл бы список вилл-исключений. В каждом добавил бы идентификаторы двух новых вилл. Сохранил бы все три файла. Это три места где легко ошибиться. Я мог бы забыть отредактировать одно из них. И тогда алерт продолжал бы литься из одного из скриптов ещё неделю. Я нашёл бы его потом когда снова откроет чат и заметит странный алерт. Это очень распространённый паттерн ошибок в системах где логика разбросана по коду.

Сейчас я сделал иначе. Я добавил одно поле в таблицу вилл в базе данных — `management_status` с возможными значениями `active` или `managed_externally`. Потом я прошёлся по всем алертным скриптам и добавил одну строку в начало каждой функции которая отправляет алерт про виллу:

if villa.management_status != 'active': return

Это одна строка. На одиннадцать символов. Во всех скриптах которые касаются этой виллы. Теперь когда я передаю виллу другой управляющей компании, я меняю одно значение в базе данных. Ставлю `management_status = 'managed_externally'` для двух вилл в одной SQL-команде. За 10 минут 74 правила умолкают. 74, потому что это количество всех условных логик которые проверяют разные состояния вилл в системе. Теперь все они ответили на одно изменение в одном месте.

Никаких хардкодов в коде. Никаких скрытых зависимостей. Единая правда в одном месте важнее чем правильно написанные скрипты в 13 разных файлах. Потому что когда бизнес-ситуация меняется (виллу передали другой компании), код должен адаптироваться в одной точке, а не в трёх.

Кейс: атомарные миграции и почему это важно

В понедельник был день рождения у моего друга. Я ему обещал подарок — голосовую колонку с интегрированным Альтроном, моим набором из 18 агентов, прямо в умное устройство для умного дома. Три дня я ковырял интеграцию маленькой ESP32-коробочки, которая работает как хаб для умной автоматизации, с Альтроном. Выловил шесть багов по дороге — от неправильного преобразования формата до задержек в сетевом запросе до проблем с кодировкой русского текста. К среде к полудню колонка проговорила анекдот про Шерлока Холмса голосом Дмитрия Жаркова. Было весело.

На той же встрече мне позвонил другой друг, Полушин. Он сказал одну фразу которая изменила мою архитектуру: «у меня бот лежит в крэш-лупе семь часов». Я зашёл в его систему чтобы разбираться что не так. Логи показывали криптографическую ошибку, и ошибка повторялась каждый цикл рестарта бота.

Копал дальше. Оказалось что предыдущий разработчик начал процесс шифрования базы данных — добавил ключ шифрования в файл переменных окружения, запустил скрипт инициализации шифрования. Потом он остановился на первом этапе, может быть понял что нужна дополнительная конфигурация. На следующий день он забыл что начал процесс, и вернулся к другим задачам. Результат: базе данных добавлена переменная что она должна быть зашифрована, но сама база осталась незашифрованной. В неопределённом состоянии посередине.

Когда бот стартует, он пытается подключиться к базе. Видит что по конфигу база должна быть зашифрована. Пытается расшифровать данные. Но данные не зашифрованы. Падает. Рестартует автоматически через 30 секунд. Снова пытается расшифровать незашифрованное. Снова падает. Это длилось 33 часа. За это время система потеряла все живые данные которые поступали — они не могли записаться в недоступную базу.

Я зашифровал базу правильно за одну сессию, поднял бота, написал ему «работает». Он обрадовался. Но дальше я пошёл в конституцию своих ботов и добавил жёсткое правило в раздел про деструктивные операции: любая многошаговая операция — миграция схемы базы данных, шифрование, смена конфига, обновление переменных окружения — должна быть либо полностью завершена за одну непрерывную сессию, либо её вообще не начинать. Нет разрывов во времени между шагами.

Потому что когда миграция разбита на два этапа с разрывом во времени (один день оперативно, второй день забыл), система оказывается в неопределённом состоянии ровно между этими этапами. И ровно там она падает. Не завтра в какой-то случайный момент. Именно там. В щели между двумя шагами миграции. Это не баг. Это математический факт архитектуры.

Метрика: 8 ассистентов, $3000 выручки, сарафан vs таргет

В среду я озвучил одну метрику которая кажется мне более важной чем месячные отчёты и квартальные планы. В октябре я запустил первого ассистента для партнёра — человека который увидел мою автоматизацию вилл и спросил «могу ли я себе такого ассистента?». С октября по май, в течение полугода, у меня 8 живых ассистентов в production у восьми разных предпринимателей. Никто из них не отключил. Никто не попросил вернуть деньги. Никто не сказал что это не работает или что вместо того чтобы нанимать агентов лучше нанять человека.

Выручка пока три тысячи долларов. Это не большие деньги для SaaS-компании. Но это не поступает от таргетированной рекламы на Facebook, не от лендинга с автоматической email-воронкой, не от продажных техник и скриптов менеджеров продаж. Это сарафан. Предприниматель видит что его ассистент работает, видит что рутина сократилась, видит что он может спать более спокойно и не проверять чат каждый час. Рекомендует знакомому предпринимателю. Тот говорит: я за это плачу? Говорит да. Тот заказывает свою версию. Это дороже чем таргет и надёжнее.

Наблюдение которое я заметил: четыре раза подряд клиент приходит с парой. Психолог с бизнесом обучения и его ассистент. Тренер по персональному развитию со студией и его менеджер по продажам. Ателье с нуждой в управлении заказами и человек который занимается логистикой. Отель с бронированиями и менеджер по продажам. Пара часто приходит потому что у них общая проблема — управлять двумя бизнесами одновременно, оба требуют много рутины, один человек не справляется. Я начал делать парный формат как отдельный продукт, настроенный на то чтобы один ассистент управлял двумя бизнесами одного клиента с синхронизацией между ними.

Финансовый дашборд: от Excel к живым данным

К вечеру четверга я добил финансовый дашборд для инвесторов портфеля. Портфель состоит из 16 действующих вилл на Бали. Инвесторы: 9 человек. Условия: у каждого свои, потому что вилла №1 покупалась в 2023 году и её инвесторы согласились на комиссию 15%, вилла №2 покупалась в 2024 году и там комиссия 18% потому что были дополнительные расходы на ремонт, вилла №3 в 2025 году и там своя ставка потому что финансирование прошло по другой схеме.

Раньше финансовый отчёт был ручной процесс на Excel. Я сидел в конце каждого месяца, открывал Excel-файл с 16 вкладками (по количеству вилл), пересчитывал за апрель все числа по каждой вилле, потом считал доли каждого инвестора, потом отправлял результат девяти инвесторам по email. Один раз в месяц. Если инвестор спрашивал что-то по числам в августе (это было пять месяцев назад) — мне нужно было переходить на другие месяцы в Excel и пересчитывать всё с нуля, потому что могли поменяться цены или условия договоров.

Сейчас дашборд читает живые данные каждый день из системы управления бронированиями. Я добавил правило которое защищает от ошибок: закрытый месяц больше не пересчитывается никогда (каждый месяц вычисляется один раз в начале следующего месяца и остаётся неизменным). Если нужна корректировка за прошлый месяц — она идёт отдельной строкой в дашборде (полная история всех изменений и причины на случай аудита). И теперь в начале каждого месяца каждый инвестор может просто открыть дашборд через browser и видеть сколько он заработал на предыдущий месяц в реальном времени. Мне больше не нужно его ежемесячно писать письмо и приложение с цифрами. Система сама говорит инвесторам сколько они заработали.

Контент как продукт: VK как полноценная платформа

В пятницу пятидневка дошла до контента. Я заметил что в моём VK четвёртые сутки подряд тишина. Никаких новых постов. Это было странно. Я же готовил статьи и материалы, они должны были публиковаться. Полез разбираться что происходит с алгоритмом публикации. Оказалось что мой скрипт публикации на ВКонтакте рубил длинные посты-агрегаты целиком за нарушение стандартов качества платформы. Текст был слишком длинный, слишком много ссылок, слишком много хештегов. Система фильтра видела это как спам и отклоняла.

Я рассматривал VK как fallback к другим платформам, как резервный выход на случай если Telegram или Instagram упадут или изменят политику. Это была архитектурная ошибка. VK — отдельная платформа со своей аудиторией, своим алгоритмом, своими стандартами и пределами длины текстов.

Я переделал подход полностью. Теперь длинные тексты режутся на атомарные идеи — каждая идея это отдельный пост на отдельный день в расписании. Каждый пост адаптируется под свой канал — что хорошо читается в Threads (короткие острые мысли в 280 символов) может убить в VK (нужны длинные обстоятельные посты, но не длиннее 700 символов за раз). Каждый пост переписывается для контекста своей аудитории. ВКонтакте теперь полноценная платформа, а не запасной выход.

Заключение: масштаб без найма

Команда у меня состоит из двух частей: я, Юрий Солар, и Альтрон — мой набор из 18 агентов которые поддерживают систему и выполняют операционные задачи. За пять дней на неделе 2-6 мая я успел: создал систему тишины для 13 ботов, закрыл миграцию двух апарт-отелей на новую управляющую компанию, сделал подарок другу (голосовую колонку с Альтроном), помог другому другу с крэш-лупом его бота, запустил восьмого платного клиента в production, довёл до ума финансовый дашборд для инвесторов, переделал контент-конвейер для VK-платформы. Восемь дел. За пять дней.

Это была бы невозможно если бы я нанимал человека на каждую из этих задач. Наёмный человек отвечает на просьбу в неделю. Агент отвечает в день. Наёмный человек отвечает про одно, может быть не понимая контекста других семи задач. Агент может переключаться между восьмью задачами параллельно, потому что он видит всю систему целиком и понимает как они связаны.

Странное чувство появляется когда между «успеть» и «не успеть» уже не стоит вопрос «нанять ли человека или автоматизировать». Встаёт совсем другой вопрос: «как попросить агента чтобы он сделал это до утра». И агент делает. До утра.

Вот вам вопрос на рефлексию: откройте пять ваших самых старых рабочих скриптов. В каждом посмотрите одно какое-то правило — например правило про отключение уведомлений для определённых кейсов, или про исключение какой-то категории данных, или про формат сообщений которые система отправляет. А теперь пересчитайте — в скольких файлах это правило прописано дважды? В трёх? В пяти? И что произойдёт если это правило завтра нужно будет изменить?

Частые вопросы

Как система тишины для ботов снижает количество уведомлений?
Вместо того чтобы каждый из 13 ботов отправлял сообщения индивидуально, я создал единый модуль, через который проходят все уведомления. Модуль знает про окно тишины (21:00–09:00 WITA, когда балийская команда спит) и копит ночные сообщения в очередь. Утром они выходят пачкой в 09:00. Никто не теряет информацию, но звуков в телефоне за ночь ноль. Раньше Тригуна просыпался 8–10 раз за ночь от разных систем.
Зачем нужно одно поле в базе вместо 74 правил в коде?
Когда я передавал две виллы другой управляющей компании, алерты о них продолжали лить в чат из трёх разных скриптов. Раньше я бы вручную отредактировал каждый скрипт — ошибка в одном месте создаёт баг в другом. Сейчас одно поле в базе `villa_status` (значение `managed_externally`) говорит всем скриптам: «не трогайте эту виллу». Все 74 правила, которые раньше были захардкодены в коде, теперь читают одно поле перед отправкой алерта. Изменение занимает 10 минут вместо часа, и нет риска что-то забыть в одном из файлов.
Почему атомарность миграций важна в операционке?
Друг показал мне бота в крэш-лупе 33 часа. Причина: предыдущий человек начал шифровать базу данных, добавил ключ шифрования в переменные окружения, но саму базу зашифровать забыл на следующий день. Результат: бот при старте пытался расшифровать незашифрованные данные и падал. Это стало правилом: любая многошаговая миграция (смена конфига, шифрование базы, смена API) должна быть либо полностью завершена за одну сессию, либо её вообще не начинать. Разбитая на два дня миграция = система в неопределённом состоянии между шагами, и именно там она падает.
Как 8 ассистентов в проде связаны с отсутствием найма?
В октябре я запустил первого ассистента для партнёра. С тех пор у меня 8 живых ассистентов у разных предпринимателей, и ни один не отключил. Это не значит что я их «продаю» в классическом смысле — это сарафан. Предприниматель видит результат, рекомендует знакомому, тот заказывает свою версию. $3000 выручки, парный формат как новый вариант. За пять дней неделя — это не таргет и лендинг, это люди кому нужен результат.
Зачем ещё финансовый дашборд если есть Excel?
У меня портфель из 16 вилл на Бали, 9 человек вложены деньги, у каждого свои условия дележа. Раньше я вручную пересчитывал Excel раз в квартал — долгий процесс, высокий риск ошибки, инвесторы ждали месяцы. Сейчас дашборд читает живые данные из системы бронирования каждый день. Закрытый месяц больше не пересчитывается (защита от ошибок), корректировки идут отдельной строкой (полная история), и в начале каждого месяца инвестор видит свой прибыль от предыдущего месяца без моего участия.

Читайте также

Подписаться на блог в Telegram

Читайте свежие кейсы об AI-автоматизации, системной архитектуре и масштабировании бизнеса.

Подписаться