Это не я, но чувствую себя примерно так же
Это не я, но чувствую себя примерно так же

Как вы, вероятно, помните по статье про вайбкодинг на MacBook Neo, программировать я не умею. Вообще. Нет, про Терминал я, конечно, слышал, но по факту никогда им не пользовался. А вот зачем нужен Git, натурально узнал только после того, как случайно затёр рабочий код старой копией и потом 2 дня разбирался, почему вдруг завелась старая версия бота, а новый функционал пропал. Тем не менее, прямо сейчас у меня крутится около десятка Telegram-ботов на одном VPS — и все рабочие, все делают полезные вещи и даже помогают зарабатывать деньги. А всё потому, что я в своё время не испугался критики настоящих прогеров и рискнул войти в вайбкодинг. Да так, что не вышел из него до сих пор. Сегодня расскажу о том, как пишу телеграм-ботов, вообще не имея представления ни о питонах, ни даже о хеллоуворлдах.

Бот, который ведёт каналы вместо живого автора

Прежде чем мы перейдём к самому интересному, небольшая вводная.

В редакции, где я работаю, есть несколько Telegram-каналов. До определённого момента их вёл отдельный человек на зарплате. Он отбирал контент, писал описания к постам и по расписанию публиковал. На один пост уходило минут 15–20, а постов в совокупности выходило штук 8–10 каждый день. Короче, работёнка — не бей лежачего. Но потом человек ушёл, новый на его место не пришёл, и нагрузку в руководстве решили распределить между авторами, в числе которых оказался и я.

Мне, естественно, перспектива работать больше, чем положено, не улыбалась, и я сделал бота. Ну, как я?.. Его сделал Claude Code. А мне просто принадлежала идея скинуть на него всю работу.

Правда, в успех я поначалу не особо верил. Поэтому я просто открыл Claude Code и спросил, реализуема ли вообще моя задумка. Клод, само собой, ответил, что да, и я написал ему примерно следующее:

“Сделай бота, которому я буду кидать ссылки на статьи, а он будет писать по ним короткие посты для моего телеграм-канала”.

Ну, а чего? Опыта-то нет.

А ведь на своем VPS можно поднять еще кое-что, созвучное с КВН
А ведь на своем VPS можно поднять еще кое-что, созвучное с КВН

Благо, Клод все разложил по шагам: получить через BotFather, арендовать VPS, поднять окружение. Он даже VPS порекомендовал: Hetzner за 5 евро, помог зарегистрироваться и настроить. Подключился по SSH, Claude Code сам установил Python, поставил aiogram, создал виртуальное окружение, настроил структуру проекта. 

Первая рабочая версия появилась в тот же вечер. Бот запускался, принимал ссылку, что-то генерировал и даже отправлял в канал. Вот только то, что он генерировал, было, мягко говоря, не то.

Что было не так с моим ботом

Прежде всего, мне не нравился стиль. Посты в канале должны были звучать в определённой манере, а бот выдавал какую-то безликую кашу. Причём самое обидное — когда я просил Claude в обычном чате написать пост по той же ссылке, получалось отлично. Прямо в точку, нужный тон, нужная подача. Но через бота выходил совершенно другой результат. Та же модель, тот же текст на входе, но на выходе небо и земля.

Я долго не мог понять, в чём дело, пока не спросил у того же Claude прямо в чате: почему, дескать, в чате получается хорошо, а через API — плохо? Оказалось, что всё дело в системном промпте. В чате у Claude есть свой встроенный набор инструкций, который задаёт тон и манеру общения. А через API бот отправляет голый запрос без какого-либо контекста, и модель отвечает максимально нейтрально. То есть нужно было написать свой системный промпт, который объяснит модели, как именно она должна писать.

Ну, я и написал. Вот только словесные правила не работали от слова совсем. Просишь его писать «коротко, дерзко, с юмором», Claude что-то генерирует, но ты все равно видишь, что это не то. То есть формально-то пост, конечно, соответствует требованиям, но на деле его стилистика совершенно не соответствовала нашему tone of voice.

Да, мой бот еще и картинки сам генерирует. Но это тема для отдельной статьи
Да, мой бот еще и картинки сам генерирует. Но это тема для отдельной статьи

Поэтому я попросил Клода в чате дать мне совет, и тот сказал, что в таких случаях хорошо работают реальные примеры, или, как он выражается, few shots. Поэтому я собрал с десяток эталонных постов и закинул в Claude Code, чтобы модель видела конкретные образцы и писала так же. 

Не сказать, что все было идеально, но из 10 постов мне нравилось штук 5-6. Тогда и родилась идея добавить кнопку «⭐ Сохранить как пример стиля», чтобы все удачные примеры попадали в обучающую выборку. Со временем примеров больше, стиль точнее, и в какой-то момент я поймал себя на мысли, что вообще перестал что-либо сохранять.

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

Весь бот в итоге разросся до 4 300 строк, но результат мне нравился. При этом ни одну из них я не написал руками. Всё делал Claude Code: я только описывал, что хочу, он генерировал код, я проверял результат.

Как бот перестал умирать и начал жить своей жизнью

Первое время бот работал довольно исправно. Во всяком случае, так мне тогда казалось. Но потом я заметил, что стоит мне закрыть ноутбук, как он больше не отвечал. А ведь в этом отчасти и была идея – сделать так, чтобы в том числе работать удаленно или в идеале не работать вообще. Поэтому такое поведение бота пугало.

Только после того, как Клод по моей просьбе провел ревизию, выяснилось, что бот работал локально, на моей машине, а на сервер его никто не выводил. 

Claude Code сам всё сделал: перенёс код на сервер, настроил автозапуск — чтобы сервер сам поднимал бота при перезагрузке и перезапускал, если тот упал. После этого бот стал жить своей жизнью. Сервер перезагружается — бот стартует. Упал — через 5 секунд поднимается. Кайф.

Поэтому и правки с тех пор делаются так: Claude Code подключается к серверу, редактирует код прямо на месте. После каждого изменения обязательно проверяет, всё ли работает, и смотрит логи. Если всё чисто — работаем дальше. Если ошибка — следующая итерация.

Этот ритуал «поправил – проверил – посмотрел логи» тоже появился не просто так: пару раз Клод деплоил сломанную версию и говорил «готово», а бот на сервере лежал. После этого я добавил правило: проверять логи ДО того, как говорить мне, что всё ок. И Клод это правило теперь выполняет.

Файл с правилами, который я пополняю после каждого факапа

Клод тоже помогал собирать информацию для этой статьи
Клод тоже помогал собирать информацию для этой статьи

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

К моему удивлению Клод сам предложил создать набор правил, которые он читает в начале каждой сессии. Опять же, появились они не сразу, а копились по мере возникновения новых проблем и моей ругачки с ИИ: Вот самые, как мне кажется, главные:

  • Сначала разведка, потом код. Не предлагать решение, пока не прочитаны все файлы, не проверены логи и статус сервиса. Появилось после того, как Клод починил одно, а сломал другое, потому что не посмотрел, как устроен остальной код.

  • Читать файлы с сервера, не локальные копии. Появилось после того, как Клод предложил фикс для версии кода, которая на сервере давно не стояла.

  • Не вставлять код одной конкретной командой. После неё бот перестаёт работать и сыплет ошибками. Полчаса разбирались, пока не откатили к предыдущей версии.

  • Не трогать то, что работает, если не просили. Появилось после того, как Клод при добавлении новой кнопки заодно переписал обработчик ошибок под старую версию библиотеки. На сервере стояла новая, и всё посыпалось.

  • Проверить логи ДО того, как сказать «готово». Несколько раз Клод деплоил сломанный код и рапортовал об успехе, а бот на сервере лежал.

Помимо этих правил, есть отдельный файл с уроками — что-то вроде базы знаний по конкретным багам. Там уже 15+ записей. Каждая — с описанием проблемы, причиной и решением. Например: API публикации возвращает «всё ок», а пост не появляется — оказалось, что Клод использовал неправильное название поля для текста. Или: один из каналов перестал получать посты, а два других работали — оказалось, что бот отправлял картинку с коротким таймаутом и без повторных попыток, и при малейшем подвисании связи пост терялся.

Клод читает оба файла в начале каждой сессии. И, надо сказать, с каждым месяцем ошибается всё реже. Дрессировка, в каком-то смысле.

Git, или как я перестал терять код

Про Git я уже упоминал в начале. Первые пару месяцев работал без него. Не потому что был самонадеянным, а потому что банально даже не знал, что такое бывает. Код жил только на сервере. А, если что-то ломалось — откатиться было некуда.

Ну и в один прекрасный день случилось то, что должно было случиться: я каким-то образом затёр свежий код старой копией. Два дня потерял, пока разбирался. Спросил Клода: как сделать, чтобы такого больше не было? Он рассказал про Git, сам создал репозиторий и написал скрипт. Одна команда — и текущий код с сервера улетает в резервную копию. Мне достаточно сказать «сохрани, починили стиль» — и готово.

Если пройдете капчу на гитхабе с первого раза – вы гений
Если пройдете капчу на гитхабе с первого раза – вы гений

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

Какие ещё проблемы вылезали в процессе

Всё, что описано выше, — это, так сказать, базовые грабли. Но были и штуки поинтереснее.

Одна из самых неприятных — когда бот публиковал текст предыдущего поста вместо нового. Случалось это не всегда, а только в случае, если я вставлял текст статьи вручную через специальную команду, а потом кидал новую ссылку. То есть ты кидаешь ссылку на статью про смартфоны, а бот генерирует пост про готику, потому что в прошлый раз была статья про готику. Я обнаружил это случайно и долго не мог понять, что происходит. Оказалось — одна строчка кода, которую Клод забыл добавить. Баг жил неделю, прежде чем я его заметил.

Другая история — с монитором ошибок. На сервере крутится отдельный скрипт, который следит за логами бота и шлёт мне уведомления, если что-то пошло не так. Идея отличная. Вот только в какой-то момент бот начал сыпать мне уведомлениями, хотя по факту всё работало нормально. Оказалось, что мелкие сбои связи с Telegram, которые бот уже обработал и пережил, всё равно записывались в лог как серьёзные ошибки. Монитор их ловил и паниковал. Пришлось попросить Клода перевести мелкие сбои в другую категорию, чтобы монитор их не трогал.

Ну и парсинг статей — отдельная песня. Куча сайтов блокирует прямые запросы: отдают пустую страницу или ошибку. Бот раньше на этом просто падал. Сейчас все устроено хитрее: если основной способ не сработал, бот автоматически пробует через сторонний сервис. А если и тот не помог — предлагает мне вставить текст вручную. До этой цепочки дошли не сразу, конечно. Сначала бот просто падал, потом научился показывать ошибку, и только потом появилась автоматическая подстраховка.

И ещё одна штука, которая до сих пор не починена: бот забывает всё при перезапуске. Монитор ошибок перезапускает его каждые несколько минут, и если в этот момент ты начал делать пост — всё, начинай заново. Клод говорит, что нужно сделать «персистентное состояние», но руки пока не дошли. Когда-нибудь, наверное, доберёмся.

Что получилось в итоге

Сейчас бот стабильно ведёт три канала и генерирует по 25-30 постов в день. Примерно каждый пятый я перегенерирую или правлю вручную — но это буквально 10 минут в день вместо трёх-четырёх часов, которые уходили раньше. И самое главное — я не стал тем человеком, которому навесили чужую работу. Точнее, навесили. Но я навесил её дальше.

Крутится всё на том же VPS за 5–7 евро. Claude Code — подписка за 100 долларов в месяц. Плюс расходы на Claude API для генерации постов — но там суммы совсем небольшие. Итого примерно 110 долларов в месяц за штуку, которая заменяет человека на зарплате.

А ещё этот бот был первым. После него я сделал ещё с десяток: один генерирует заголовки для статей на сайте с учётом поисковых запросов, другой ведёт учёт моих доходов и расходов по скриншотам из банковского приложения, третий делает карточки для соцсетей. Список длинный, но об этом, наверное, в другой раз.