Прежде чем начну, вот репозиторий. Я не хочу ничего монетизировать, продавать или куда-то вас вести (в секту имени меня, например). Если этот бот, идея или даже отдельные куски кода окажутся кому-то полезны в жизни - я сделал чей-то день и, возможно, чья-то жизнь стала немного ярче.
Начнем.
Есть слова, которые опасно произносить рядом с правильными людьми.
Например, "хумус".
Потому что в какой-то момент выясняется, что сказал ты его неосторожно, а услышали это слово как "Хорус". И вот ты уже не обсуждаешь еду. Ты уже стоишь в дверях собственной кухни и слушаешь невероятно экспрессивный, абсолютно незапрошенный, но внутренне очень стройный и, что самое важное, структурированный доклад о том, кто кого предал, почему все пошло не туда, почему бюрократия все сломала и с какой книги вообще надо было начинать.

С домашними делами происходит почти то же самое.
Все начинается с безобидного "надо не забыть купить фильтр". Потом эта мысль куда-то уезжает. Потом кто-то уверен, что это уже где-то записано. Потом кто-то другой уверен, что это точно помнит. Потом фильтр не куплен, это нигде не зафиксировано, а вся домашне-бытовая система, оказывается, держалась на древней и крайне ненадежной технологии "да это же очевидно, как такое забыть".
Как оказалось, очень даже можно.
Так у меня и появился Telegram-бот для домашних дел. Не "еще один таск-менеджер". Не "домашняя ERP". И точно не попытка натянуть корпоративный подход на обычную жизнь, в которой и без того хватает процессов, дедлайнов и систем контроля, только это все у нас называется иначе. Хотелось сделать не инструмент "для учета", а инструмент, который просто помогает не терять бытовые мелочи, пока голова занята чем-то поважнее.
В итоге получился отдельный бот для групповых чатов (у нас это я и жена), который существует ровно для одной цели: быстро превращать бытовой шум в понятный и общий список дел.
Написал сообщение - дело появилось в списке. Указал человека - бот сразу закрепил это за ним. Открыл меню - увидел текущее состояние. Скинул чек - бот его разобрал. Богомерзкая голосовуха - получил новую запись без печати. И главное, все это сразу видно всем участникам чата. Не нужно пересылать, напоминать, уточнять, у кого это "точно было записано".
Из этой очень приземленной боли и вырос вполне настоящий маленький сервис.
Бытовые вещи проигрывают не по важности, а по трению
С рабочими системами все более-менее понятно. У них есть процессы, дисциплина, привычка, иногда страх (мое любимое), иногда календарь, иногда менеджер (мое любимое #2). У домашних дел с этим заметно хуже.
Они появляются между делом. В магазине, на кухне, в машине, в лифте, с пакетами в руках, в момент, когда внимание уже убежало дальше. Мысль "надо бы это не забыть" живет секунд десять. После этого она либо куда-то попадает, либо исчезает. И это стреляет тебе в ногу.
Айфон у жены? Андроид у меня? Общая экосистема, условно, отсутствует. Да, есть примерно миллиард решений с заметками, начиная от простейших notepad-like вещей до гигантских SaaS. И вот тут выясняется простая вещь: если для фиксации бытовой мелочи нужно открыть отдельное приложение, выбрать нужный раздел, создать новую запись, что-то там дооформить и не забыть сохранить, то значительная часть таких вещей просто не будет зафиксирована.
Не потому что кто-то ленивый. А потому что бытовая реальность плохо сочетается с лишними действиями.
Поэтому у меня было очень простое требование к инструменту: путь "вспомнил -> записал" должен быть короче, чем путь "вспомнил -> отвлекся -> забыл". А если дело касается не одного человека, а общей рутины (пусть даже из двух людей), то оно еще и должно сразу оказаться в общем поле зрения. Без пересылок, без скриншотов, без устного "я тебе потом скину".
Почему бот живет в отдельном групповом чате
Эта деталь звучит маленькой, но на деле определяет почти весь UX.
Если бот должен превращать обычный текст в записи списка дел, он не может жить в произвольном потоке сообщений. Иначе очень быстро начинается цирк с конями: часть сообщений - реальные бытовые дела, часть - просто разговор, часть - случайные реплики, а часть внезапно оказывается в списке просто потому, что бот слишком буквально понял мир вокруг себя.
Поэтому бот живет в собственном групповом чате. Не в личке и не в "обычном семейном чате с ботом где-то сбоку", а именно в отдельном пространстве, которое существует для бытовой координации. За счет этого любое входящее сообщение по умолчанию можно воспринимать как полезное действие, а не как шум.
Это и оказалось главным UX-решением всего проекта.
Вместо длинного ритуала получается почти разговорный интерфейс. Написал "Купить фильтр для воды" - дело попало в список. Написал "Забрать заказ (Алексей)" - запись сразу закрепилась за человеком. Написал "Напомнить про ветклинику" - мысль не пропала, а стала частью общей картины.
И здесь важна не только скорость. Важна еще и видимость. Это не личный список, который существует у кого-то в голове или в каком-то приложении. Это общий список дел, который видят все участники чата. Для бытовых сценариев это оказалось намного полезнее, чем строгая персонализация. Меньше потерь на координации. Меньше "я думал, это у тебя записано". Меньше домашней энтропии, которая живет только в чужой памяти.
Что получилось под капотом
Технически это Telegram-бот на Python 3.12, aiogram 3 и SQLite, развернутый на Ubuntu Server. Архитектурно - модульный монолит (важно (!!!) и чуть ниже я опишу вам почему).
Я сознательно не стал строить из этого микросервисный полигон "на вырост". Для домашнего инструмента важнее не возможность красиво рассказать про масштабирование, а способность быстро понять, как все устроено, без боли доработать и не проклинать себя через полгода.
Структура при этом вполне взрослая. Есть входная точка, которая поднимает приложение, роутеры и фоновые циклы. Есть слой handlers для Telegram-событий, callback-ов и flow-сценариев. Есть services с бизнес-логикой (не помню, как в быту это иначе называется) и интеграциями. Есть отдельный слой работы с БД. Есть фоновые циклы для напоминаний, recurring-механики, обновления закрепленного списка и хозяйственных задач вроде очистки старых записей.
Это тот редкий случай, когда скучная архитектура оказывается лучшей. Она не производит большого впечатления на диаграмме, зато в жизни работает именно так, как от нее и ждешь.
Самая полезная идея оказалась самой простой
Главное, что сделал бот полезным, звучит почти слишком просто: обычный текст автоматически фиксируется как новое домашнее дело.
Пользователь пишет:
"Купить фильтр для воды"
"Заказать наполнитель"
"Записаться на шиномонтаж"
И бот заносит это в список дел.
Если в тексте есть маркер назначения, например "(Алексей)", запись сразу закрепляется за конкретным человеком. После этого бот подтверждает действие реакцией на сообщение, а закрепленный список обновляется.
На бумаге "любой текст становится делом" звучит как потенциальная катастрофа. На практике это работает именно потому, что бот живет в отдельном групповом чате со специальной ролью. Это не произвольный поток общения. Это пространство, где входящее сообщение почти всегда и есть полезное действие.
Конечно, без ограничений такое решение быстро бы развалилось. Бот не должен пытаться делать запись из команды, ссылки, вложения или промежуточного шага другого сценария. Поэтому "любой текст" в коде всегда означает "любой текст, который не попал в понятный набор исключений". И именно этот набор исключений на практике важнее половины всей остальной магии.
Почему понадобилось /menu
Когда у бота появляется больше нескольких функций, одних команд начинает не хватать. Даже если ты их помнишь (а зачем нам их вообще помнить? Других дел нет?).
Сначала кажется, что достаточно нескольких штук вроде /list, /buy, /history и еще пары команд на всякий случай. Потом появляются напоминания, recurring, чеки, голосовой ввод, погода, датчики, AI-сценарии, и внезапно выясняется, что никто не хочет держать в голове CLI для бытовой жизни.
Так появился /menu.
Это единая панель, которая редактируется на месте через edit_text и edit_reply_markup. В ней собраны основные сценарии: список дел, покупки, чеки, история, напоминания, погода, домашние сенсоры и прочие блоки.
Польза здесь даже не столько в красоте, сколько в том, что чат не засоряется служебными сообщениями. Пользователь по сути работает внутри одного обновляемого экрана, а не оставляет за собой длинный след из "выберите пункт", "подтвердите действие", "вот результат", "нажмите назад".
Для мессенджера это критично. Если интерфейс не бережет чат, он очень быстро начинает раздражать.
Закрепленный список как "точка правды"
Одна из самых полезных частей системы - ежедневный закрепленный список дел.
Он работает как общая точка правды по текущему состоянию: что открыто, что уже сделано, что закреплено за кем-то, что висит без исполнителя. Вместо того чтобы искать старые сообщения или пытаться восстановить контекст по памяти, участники чата в любой момент видят актуальную картину.
И это не декоративная функция. В групповом сценарии общая видимость вообще важнее, чем кажется по описанию. Бот не просто сохраняет очередную бытовую мелочь. Он делает ее частью общего, постоянно доступного состояния.
Для домашних дел это оказалось не менее важно, чем сама скорость записи.
Когда бот перестал быть просто списком дел
(Если не планируете использовать OpenAI токен и реализация не интересна - скипайте этот момент).
Это произошло в тот момент, когда в нем появился импорт чеков.
Пользователь может прислать фото, PDF, ссылку или просто текст чека. Дальше начинается уже не "бот ответил сообщением", а вполне нормальный pipeline: извлечение текста, попытка детерминированно определить дату покупки, передача содержимого в GPT для парсинга в структурированный JSON, сохранение чека и позиций в БД, нормализация количества, единиц и цен.
Здесь важен порядок доверия к данным. Если дату покупки удалось извлечь надежно и детерминированно, используется именно она. Если нет, можно взять дату из ответа модели. И только в самом конце остается техническое время создания записи. Это кажется мелочью, но именно на таких "ладно, и так сойдет" аналитика потом начинает тихо врать.
Сами позиции в чеках тоже сохраняются не как безликий текст "молоко 1л". Они раскладываются на нормализованные поля: ключ продукта, количество, размер упаковки, базовую единицу, цену за единицу и так далее. После этого с ними уже можно делать что-то полезное.
Например, смотреть траты за период, видеть топ товаров, сравнивать один период с другим, искать конкретный продукт и смотреть историю его цены. И вот здесь бот внезапно перестает быть просто "куда я записываю домашние дела". Он начинает давать наблюдаемость. Не в пафосном смысле, а в очень бытовом: "почему именно это мы опять купили так дорого?" и "мы точно берем это настолько часто, как нам кажется?"
Список покупок, recurring и голосовой ввод
Как и любой полезный инструмент, бот довольно быстро начал обрастать не "фичами", а продолжениями исходной идеи.
Нужен был список покупок - появился отдельный контур ToBuy со свободным вводом, разбором количества и единиц, CRUD-операциями и доступом через текст и меню.
Нужны были повторяющиеся домашние дела - появился recurring-слой с хранением расписаний и runtime-движком, который в нужный момент материализует повторяющиеся штуки в обычные записи списка дел. Это важно: они не живут в какой-то особой параллельной реальности. В нужный момент они становятся тем же самым "обычным делом", с которым уже умеют работать остальные части системы.
Нужен был быстрый мобильный сценарий - появился голосовой ввод. Голосовые сообщения уезжают во внешний STT backend, распознаются и превращаются в запись в списке. Это одна из тех функций, которые не выглядят особенно эффектно в описании, но в реальной жизни оказываются очень живыми. Потому что бытовая мысль обожает приходить именно тогда, когда печатать неудобно.
Где здесь GPT, а где обычный код
Мне очень не хотелось превращать проект в выставку "смотрите, тут тоже есть AI". Поэтому GPT используется только там, где он действительно снимает боль.
Это разбор чеков, разбор рецептов в список покупок, факт дня, часть AI-команд и /bot, если не включена локальная LLM.
Во всех остальных местах лучше работает обычная детерминированная логика.
Это, пожалуй, один из самых полезных выводов по всему проекту. Если у тебя есть неструктурированный хаос на входе, модель отлично помогает превратить его в структуру. Но если ты начинаешь доверять модели как единственному источнику правды, то довольно быстро получаешь очень уверенную, очень внятную и очень неправильную, безоткатную (проще кинуть спичку и уйти в закат) ошибку.
Что не входит "в коробку"
Нет, бот не поднимает локальную LLM силой желания. Не собирает телеметрию с датчиков из воздуха. И не оплачивает вызовы OpenAI силой характера.
У него есть базовое ядро, которое запускается довольно просто: Telegram-токен, разрешенные user ID, Python, SQLite, и можно жить. Но часть сценариев опирается на внешние зависимости. Для AI-функций нужен OPENAI_API_KEY. Он требуется для импорта чеков, разбора рецептов, факта дня и части AI-сценариев. Не для "всего бота", а именно для тех возможностей, где есть вызовы OpenAI. Ollama тоже поддерживается, но не обязательна. Локальная LLM нужна только если хочется гонять /bot через собственную модель. Блок домашних сенсоров - вообще отдельный интеграционный контур. Бот не собирает температуру и влажность сам. Он читает уже подготовленный snapshot с BLE-данными в формате Prometheus text snapshot (сервис написан и поднят мной отдельно и, когда-нибудь, возможно, я сделаю паблик репо, потому что это было невероятно весело).
Подводные камни, которые оказались интереснее, чем хотелось
Сделать Telegram-бота легко. Сделать Telegram-бота, который не бесит через месяц ежедневного использования, заметно сложнее.
Первый подводный камень - агрессивность "умного" поведения. Автоматически превращать текст в запись списка дел очень удобно, но только пока бот точно понимает, где текст действительно означает домашнее дело, а где это часть другого сценария. Стоит недожать фильтрацию исключений, и начинается сюр: поисковый запрос внезапно попадает в список дел, промежуточный шаг flow тоже туда попадает, случайный ввод оказывается важной бытовой записью, кони, люди и вот это все. Такие баги не выглядят страшно в коде, но доверие к интерфейсу ломают моментально.
Второй - Telegram сам по себе не особенно щедр на интерфейсные свободы (я вообще не хотел тут писать это слово, но ладно). Лимиты на callback_data, особенности редактирования сообщений, необходимость жить внутри inline-кнопок, какие-то пограничные случаи с правами и удалением сообщений - все это быстро напоминает, что ты строишь интерфейс внутри мессенджера, который вообще-то не обещал нам полноценную UI-платформу.
Третий - LLM нужно очень четко знать свое место. Модели отлично помогают, когда нужно распарсить хаос во что-то похожее на данные. Но как только начинаешь верить им без жесткого каркаса из нормализации и проверок, быстро получаешь проблемы с датами, единицами измерения и аналитикой.
Четвертый - фоновая автоматизация кажется проще, чем есть на самом деле. Напоминания, recurring, ежедневное обновление закрепленного списка, debounce списка, очистка старых записей - снаружи это выглядит как "ну там несколько циклов". Внутри довольно быстро начинается полная срака и история про лишние импорты, idempotency, повторы, пропуски и необходимость не спамить Telegram лишними обновлениями.
Почему SQLite здесь не стыдно
Есть проекты, где SQLite - временная уступка. Здесь это, скорее, осознанное инженерное решение.
Для такого масштаба она дает почти все, что нужно: простой запуск, понятный backup, минимальный операционный overhead, локальное и предсказуемое хранение, достаточную производительность и возможность делать вполне вменяемые агрегаты по чекам, списку дел и истории.
Иногда зрелость не в том, чтобы насильно принести в проект Postgres, Redis и половину DevOps-конференции - у меня есть теоретический опыт в DBA (спасибо очень толковым коллегам) и Observability (еще раз спасибо очень толковым коллегам). Иногда зрелость - это вовремя понять, что SQLite решает задачу лучше именно тем, что не требует экстра движений.
Деплой без театра
Развернуто все это на Ubuntu Server под systemd. При желании можно использовать Docker Compose (запуск описан в доке, но писал я его из головы и не пробовал в живую). Локальные данные живут рядом, SQLite очевидно переживает рестарты, конфигурация собирается через .env, а минимальный набор для старта довольно скромный.
И мне кажется, это тоже часть качества проекта.
Если для бота, который должен помогать не забыть купить фильтр или записаться на шиномонтаж, сначала нужно построить инфраструктуру уровня "маленькая внутренняя платформа", значит, где-то по пути инженерная мысль слегка обогнала реальность.
Что оказалось самым важным
Не GPT. Не aiogram. Не SQLite. И даже не сам факт, что это Telegram-бот.
Самым важным оказался другой принцип: бытовой инструмент не должен звучать как работа и не должен требовать отдельной дисциплины. Он должен работать на инерции. На естественном действии. На минимальном числе шагов.
Именно поэтому реально полезными оказались не самые громкие возможности, а самые органичные: отдельный групповой чат, обычный текст как вход, общий список дел, закрепленный список как точка правды, меню вместо потока служебных сообщений, recurring без отдельной сущностной вселенной, чеки как источник данных и голосовой ввод там, где руки заняты.
Что дальше
Дальше - спокойное приведение миров к согласию.
То есть нормальная инженерная работа: улучшать UX, упрощать сценарии, усиливать аналитику, расширять тестовое покрытие, аккуратнее обрабатывать крайние случаи и продолжать вычищать все места, где "почти удобно" пока еще не равно "удобно каждый день".
И, пожалуй, это и есть главный итог проекта. Он вырос не из любви к ботам, не из желания сделать "что-нибудь AI-powered" и не из мечты построить домашнюю платформу автоматизации. Он вырос из очень простой бытовой злости на потерянные мелочи, которые постоянно съедали внимание и приносили жопаболь.
Отдельный момент про AI в разработке самого бота
Раз уж в статье уже есть GPT, честно скажу и про другую сторону вопроса. В разработке самого бота я совершенно спокойно использовал Claude и Codex для быстрофиксов, рутины, мелких рефакторингов, прохода по шаблонным кускам кода и прочих вещей, на которые раньше слишком легко уходили лишние когнитивные силы и которые я читал уже миллион раз.
Я это не скрываю и не собираюсь делать вид, что весь код был написан в один присест при свете инженерной доблести и внутреннего огня. Я искренне считаю, что спать 8 часов - великолепно. А оставшееся время у меня и так прекрасно съедает любимая работа. И если часть рутинной разработки можно ускорить без потери контроля над архитектурой, качеством и смыслом - я с удовольствием это делаю.
Для меня это вообще не история про "AI написал проект за меня". Архитектура, логика, ограничения, сценарии, подводные камни и бесконечное количество "нет, вот тут так делать нельзя" все равно остаются на мне и моем скилле. Просто теперь часть рутины можно не героически проживать, а делегировать. И, честно говоря, это сильно повышает шансы, что у тебя останутся силы не только на основную работу, но и на собственную жизнь. Собственно, в моем случае так и получилось: любимая работа продолжает забирать много внимания, но хотя бы запоминание домашних дел больше не пытается доесть то, что осталось.
И раз уж обычно в этом месте люди оставляют ссылку на Telegram-канал с авторскими мыслями, сделаю по-своему.
Если вы ищете работу в IT, свободно говорите на английском, являетесь технарем или около того, любите людей и имеете опыт на руководящей позиции лида направления технической поддержки - именно внутренней поддержки сотрудников внутри технической компании - мы вполне можем обсудить ваш поиск работы и, возможно, стать коллегами. Вместо канала с гениальными мыслями положу сюда маленький кусочек найма к себе в департамент.
Мне кажется, это честнее и потенциально полезнее.
