Знаете, в чём проблема большинства гайдов и курсов, которые обещают научить всему и сразу — да ещё и устроить на работу? Часто они учат примитиву, выдавая это за качественный контент. В итоге появляется много низкокачественного кода: на первый взгляд он работает, но в реальности трудно поддерживается.
Если в проекте нет структуры, он быстро превращается в кашу. Каждая доработка — это не отдельный продуманный модуль, а «приматывание новых кусков кода синей изолентой» с мыслью: «хоть бы не сломалось». Для новичка это особенно опасно: кажется, что всё нормально, пока проект маленький, но при росте даже простые изменения начинают занимать часы и ломать соседние части.
Вы наверняка задаётесь вопросом: «Почему рубрика называется “ИИ бот-модератор”, а автор тут рассказывает про качество кода?» На самом деле, всё связано.
Telegram-бот для группы — отличный пример проекта, который очень быстро обрастает фичами: команды, настройки, роли, интеграции, хранение данных, логирование, админка, модерация, ИИ и т.д. Если делать всё “в одном файле”, это почти гарантированно закончится болью. Поэтому в этой рубрике мы будем строить бота так, чтобы его можно было развивать: добавлять функциональность без постоянного страха «сломать всё».
В этом цикле статей:
- Напишем многофункционального Telegram-бота для групп с ИИ.
- Напишем микросервис с MCP-сервером на FastAPI (поясню, что это и зачем он нужен, когда дойдём до интеграции).
- Познакомимся с архитектурой, близкой к DDD (Domain Driven Design): будем отделять “бизнес-логику” от инфраструктуры, чтобы код легче читался и расширялся.
- Применим современные инструменты, вроде
uvиdishka. - А ещё: Docker, PostgreSQL и многое другое.
Всё задуманное невозможно уместить в одной статье (ну или она будет на десятки тысяч символов). Подписывайтесь на наш Telegram-канал «Код на салфетке», чтобы следить за новыми частями и другими интересными статьями!
🤫А ещё! У нас скоро стартует новогодний розыгрыш!🤫
У меня уже есть базовый список функций, которые хочу реализовать в рамках цикла, но я буду рад вашим предложениям — особенно если вы уже сталкивались с модерацией в группах и знаете, что реально нужно.
Важное примечание
Это не курс “от А до Я”, а цикл статей, где я шаг за шагом буду показывать процесс разработки, дополняя его небольшими вставками теории — ровно в тех местах, где она нужна для понимания решений.
Я не ставлю целью дать единственно верный подход: это мой взгляд на процесс разработки, и он может отличаться от вашего. Уверен, опытные специалисты найдут спорные места или ошибки — и это нормально. Но для новичков этот материал должен стать хорошей опорой, чтобы увидеть разработку не в формате “всё в одном main.py”, а в виде проекта, который можно поддерживать и развивать.
Я не буду глубоко уходить в теорию и буду держать фокус на практике. Поэтому если вы хотите, чтобы я отдельно раскрыл какую-то тему (например, DDD-подход, слои приложения, транзакции, тестирование, миграции, очереди, логирование) — пишите в комментариях, и я вынесу это в отдельную статью.
Контроль версий - git
Контроль версий — одна из важнейших вещей в разработке. Он помогает не только «сохранять файлы», а управлять изменениями в проекте так, чтобы можно было безопасно экспериментировать и возвращаться к рабочему состоянию.
Система контроля версий позволяет:
- Хранить историю изменений файлов (кто, что и когда поменял).
- Откатывать неудачные изменения.
- Работать с ветками для параллельной разработки разных фич.
- Объединять (мержить) изменения между ветками.
Систем контроля версий существует много, но стандартом в индустрии считается git. Его создал Линус Торвальдс — автор ядра Linux. Важно не путать: Git — это инструмент, а GitHub/GitLab — сервисы, где часто хранят репозитории и ведут совместную работу.
Установка git
Linux
Если вы используете Linux, то, скорее всего, git уже установлен. Проверить можно командой:
git --versionЕсли Git не установлен, проще всего поставить его через менеджер пакетов вашего дистрибутива (способы отличаются, поэтому ниже оставляю универсальную ссылку).
Windows
На Windows установить Git можно двумя основными способами:
- Скачать и установить Git for Windows с официальной страницы: https://git-scm.com/download/win
- Установить через
winget. Для этого откройте PowerShell и выполните команду:
winget install --id Git.Git -e --source wingetMacOS
На macOS сначала проверьте, установлен ли git:
git --versionЕсли Git не установлен, поставьте одним из способов:
- Через Xcode Command Line Tools:
xcode-select --install - Через Homebrew:
brew install git
Другое
Инструкции для всех ОС есть на официальной странице:
https://git-scm.com/install/
Основные команды git
Первоначальная настройка
Чтобы Git мог «подписывать» ваши коммиты (указывать автора), нужно один раз указать имя и почту. Выполните в терминале:
# Установить имя пользователя
git config --global user.name "Имя"
# Установить электронную почту
git config --global user.email "email@example.com"Эти значения попадут в метаданные коммитов. Обычно почта должна совпадать с почтой аккаунта на GitHub/GitLab (если вы планируете пушить туда), но это не строго обязательно.
Работа с репозиторием
git init— инициализирует репозиторий в текущей директории (создаёт папку.git).git clone <url>— копирует удалённый репозиторий (например, с GitHub) на ваш компьютер.git status— показывает состояние репозитория: какие файлы изменены, какие подготовлены к коммиту и т.д.
Добавление и фиксация изменений
Тут полезно помнить про два «слоя»: рабочая директория (ваши файлы) и индекс/стейджинг (то, что пойдёт в следующий коммит).
git add <файл>— добавляет изменения указанного файла в индекс (подготовка к коммиту).git add .— добавляет в индекс все изменённые и новые файлы в текущей директории.git commit -m "сообщение"— создаёт коммит из того, что лежит в индексе.
Если хотите посмотреть, что именно уже попало в индекс, используйте git diff --staged (часто очень спасает от случайных коммитов).
Просмотр истории и различий
git log— показывает историю коммитов (автор, дата, сообщение, хеш).git log --oneline --graph --all— компактный лог со «схемой» веток (удобно смотреть структуру).git diff— показывает изменения, которые ещё не добавлены в индекс (строки до/после).
Работа с ветками
git branch— список локальных веток и текущая ветка.git branch <имя>— создаёт новую ветку.git checkout <ветка>— переключается на ветку (старый универсальный стиль).git switch <ветка>— современная команда для переключения веток.git switch -c <ветка>— создать новую ветку и сразу на неё перейти.
Обновление и отправка на удалённый репозиторий
git remote -v— показывает привязанные удалённые репозитории (обычноorigin).git pull— забирает изменения с сервера и пытается влить их в текущую ветку.git push— отправляет локальные коммиты на сервер (по умолчанию в текущую ветку).git push -u origin <ветка>— первый пуш ветки: создаёт ветку на сервере и связывает её с локальной.
Для понимания: pull = «забрать + слить». Иногда удобнее делать это в два шага через git fetch (забрать без слияния), но для простых сценариев pull достаточно.
Отмена и возврат изменений (простые случаи)
git restore <файл>— отменяет изменения в рабочей директории, возвращая файл к версии из последнего коммита.git restore --staged <файл>— убирает файл из индекса, но оставляет изменения в рабочей директории.git reset --hard HEAD— полностью откатывает рабочую директорию и индекс к последнему коммиту.
С reset --hard будьте осторожны: незакоммиченные изменения будут потеряны. Если сомневаетесь — лучше сначала посмотрите git status и git diff.
Игнорирование "мусора" - .gitignore
Далеко не все файлы и директории стоит добавлять в репозиторий. Обычно игнорируют:
- Виртуальное окружение
.venv— это локальные зависимости и бинарники, они у каждого могут отличаться и должны ставиться заново изpyproject.toml/requirements.txt. - Кэш Python
__pycache__/и файлы*.pyc— это автоматически генерируемые артефакты, в репозитории они не нужны. - Настройки IDE
.idea/или.vscode/— это персональные настройки редактора (часто отличаются у разных людей). - Файлы окружения
.env, логи, временные файлы, сборочные артефакты и т.п. — их либо генерирует приложение, либо они содержат локальные/секретные данные.
.gitignore — это текстовый файл со списком шаблонов, по которым Git решает, какие файлы и папки не отслеживать. Он начинается с точки (значит, файл скрытый) и не имеет расширения.
Важный нюанс: .gitignore не удаляет файлы, которые уже были добавлены в репозиторий ранее. Если файл уже отслеживается, его нужно отдельно «разотслеживать», например: git rm --cached <файл>.
Основные элементы синтаксиса
- Одна строка — один шаблон (путь, маска или правило).
- Пустые строки игнорируются.
- Строки, начинающиеся с
#, — комментарии. *— «любой набор символов»,?— «один символ».- Завершающий
/означает директорию:build/— игнорировать папкуbuild. - Ведущий
/привязывает правило к корню репозитория:/config.json. **— любые подкаталоги:logs/**/debug.log.!в начале строки отменяет игнорирование (исключение из правила):
*.log
!keep.logМенеджер проекта и виртуальное окружение
Любой проект начинается с выбора менеджера пакетов/проекта и создания виртуального окружения.
Вместе они отвечают за «быт» проекта:
- Установка библиотек.
- Разрешение зависимостей.
- Запуск команд в окружении проекта.
- Управление версией Python (когда это поддерживает выбранный инструмент).
Есть и другие возможности, но сейчас они не так важны.
В Python часть задач закрывают встроенные инструменты вроде pip и venv, а часть — отдельные утилиты, которые нужно ставить и подружить между собой. uv во многом снимает эту боль: это быстрый менеджер пакетов и проектов для Python, написанный на Rust.
uv можно воспринимать как «комбайн» для управления проектом:
- Устанавливает и обновляет зависимости проекта.
- Создаёт и управляет виртуальным окружением (по умолчанию часто используется папка
.venv). - Умеет устанавливать и выбирать версии Python (например, через
uv python install, а также через закрепление версии в.python-version). - Работает с lock-файлом
uv.lock, чтобы окружение было воспроизводимым (особенно полезно в команде и на CI). - Может инициализировать VCS при создании проекта (например, Git через опции
uv init).
Установка uv
Установить uv можно двумя способами.
- Через standalone-инсталлятор (рекомендуемый вариант):
# Linux или MacOS - выполнить в терминале
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows - выполнить в PowerShell
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"- Глобально через
pip(если так привычнее):
# Выплнить в терминале или PowerShell
pip install uvОсновные команды
Работа с проектами
uv init— инициализирует новый Python‑проект в текущей папке (создаётpyproject.tomlи базовую структуру).uv init my-project— создаёт директориюmy-projectи инициализирует проект внутри неё.uv init --package my-project— создаёт «пакетный» проект и используетsrc-layout (часто удобный и более безопасный для импорта кода).
Управление зависимостями
uv add <пакет>— добавляет зависимость в проект (обновляетpyproject.tomlи ставит пакет в окружение).uv add --dev <пакет>— добавляет зависимость для разработки (например, линтеры/тесты).uv remove <пакет>— удаляет пакет из зависимостей проекта и из окружения.
Запуск кода в окружении проекта
uv run <команда> — запускает команду в окружении проекта (и при необходимости синхронизирует зависимости/окружение).
- uv run python main.py
- uv run pytest
uv run -- <команда с аргументами>— позволяет явно отделить опцииuvот аргументов запускаемой команды.
Синхронизация окружения
uv sync— приводит окружение проекта в соответствие с зависимостями изpyproject.tomlи lock-файлаuv.lock.
Сам uv
uv self update- обновляетuvдо последней версии.uv self version- показывает версию установленногоuv.
Линтеры кода - pre-commit
Ещё один полезный класс инструментов в разработке — всевозможные линтеры и форматтеры.
Линтеры — это программы для статического анализа кода. Они проверяют исходники по набору правил: от стилистики и форматирования до поиска потенциальных ошибок и «подозрительных» мест. Важно: линтеры не запускают проект, а анализируют только код. Особенно полезно это в командной разработке: меньше шансов закоммитить проблемный код, и у всех участников получается единый стиль.
Линтеры можно ставить и запускать по отдельности, но удобнее пользоваться фреймворком pre-commit.
pre-commit — это фреймворк для управления Git‑хуками. Он подключается к git commit и автоматически запускает набор проверок (хуков), которые вы описываете в конфигурации. Если какая-то проверка не пройдена, коммит прерывается — пока вы не исправите ошибки (или не отключите/настроите правило). Это помогает не тащить в репозиторий «грязный» или явно ошибочный код.
Установка pre-commit и запуск
Установите pre-commit как dev‑зависимость, находясь в директории проекта:
uv add --dev pre-commitДальше нужно сделать две вещи:
- Создать конфиг
.pre-commit-config.yamlв корне проекта — без негоpre-commitпросто не будет знать, что запускать. - Установить Git‑хук:
uv run pre-commit installОбратите внимание! Для установки хуков должен быть инициализирован git‑репозиторий (git init), иначе pre-commit не сможет «подцепиться» к коммитам.
Помимо автоматического запуска при коммите есть и ручной запуск — удобно, чтобы прогнать проверки перед первым коммитом или после больших правок:
uv run pre-commit run --allОбратите внимание! По умолчанию pre-commit проверяет файлы, которые попадают в коммит (обычно — проиндексированные, то есть добавленные через git add). Если файл не добавлен в индекс, он, как правило, не будет проверяться — сначала добавьте его в staging или запускайте проверки по всем файлам через --all-files.
Конфигурация pre-commit
Запускаемые проверки (хуки) для pre-commit описываются в файле .pre-commit-config.yaml, который лежит в корне проекта — обычно рядом с pyproject.toml.
.pre-commit-config.yaml — это YAML-файл, где на верхнем уровне используется список repos, а внутри каждого репозитория перечисляются hooks.
Структура верхнего уровня
repos:— список источников хуков (чаще всего это Git-репозитории).- Дополнительно встречаются опции вроде
default_stages, но на старте достаточно пониматьrepos.
Простейший пример
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixerПоля внутри repos
У каждого элемента в repos обычно есть:
repo— URL (или путь) к репозиторию с хуками.rev— версия/тег/коммит (почти всегда стоит фиксировать версию, а неmain).hooks— список хуков из этого репозитория.
Поля внутри hooks
Каждый хук задаётся набором параметров, самые полезные:
id— идентификатор хука (обязателен).args— список аргументов, которые будут переданы хуку.files/types— какие файлы проверять.exclude— какие файлы/пути исключить.stages— на каких стадиях Git-хуков запускать (обычно и так достаточно дефолта).
Пример конфигурации
Ниже пример «базы», которую обычно хватает, чтобы сразу почувствовать пользу: мелкие проверки файлов + авто-апгрейд синтаксиса + линтинг/форматирование Python-кода.
Хуки из pre-commit-hooks
Подключён стандартный набор готовых хуков из pre-commit-hooks, который:
- убирает лишние пробелы в конце строк (
trailing-whitespace), следит за пустой строкой в конце файла (end-of-file-fixer) и предотвращает конфликты имён файлов на разных ОС (check-case-conflict); - проверяет YAML‑файлы на корректный синтаксис (
check-yaml), а также не даёт случайно закоммитить маркеры незавершённых merge‑конфликтов (check-merge-conflict).
Хук pyupgrade
Репозиторий asottile/pyupgrade добавляет хук pyupgrade, который переписывает код на более современный синтаксис Python.
Флаг --py313-plus говорит, что код должен соответствовать возможностям Python 3.13 и новее, поэтому инструмент может смело применять самые свежие упрощения и оптимизации.
Хук ruff
Репозиторий astral-sh/ruff-pre-commit добавляет ruff-check — быстрый линтер и форматер для Python. Аргументы --fix и --line-length=120 включают автоматическое исправление найденных проблем и задают максимальную длину строки в 120 символов, чтобы код соответствовал выбранному стилю.
Хук ty
Репозиторий vel/ty-pre-commit подключает хук ty-check, который запускает ty — новый сверхбыстрый проверщик типов для Python. ty выполняет статическую проверку аннотаций типов, быстро находит несоответствия и потенциальные ошибки в типах, что повышает надёжность кода и делает работу с типизированным Python комфортнее даже в больших проектах.
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: check-yaml
- id: check-case-conflict
- id: check-merge-conflict
- id: end-of-file-fixer
- repo: https://github.com/asottile/pyupgrade
rev: v3.21.2
hooks:
- id: pyupgrade
args: [ --py313-plus ]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.13.0
hooks:
- id: ruff-check
args: [ "--fix", "--line-length=120" ]
- repo: https://foundry.fsky.io/vel/ty-pre-commit
rev: 0.0.3
hooks:
- id: ty-checkУниверсальный запуск команд в make
Последний в этой статье, но не по значению инструмент, который мы будем активно применять — это make.
make — консольная утилита для выполнения команд и их последовательностей, пришедшая из мира C/C++ (исторически — для сборки проектов). Сейчас make часто используют как универсальный task-runner: удобные «шорткаты» для запуска проекта, линтеров, миграций и других регулярных действий.
Проще говоря, make позволяет один раз описать нужные проекту команды в одном файле — и потом вызывать их коротко и одинаково у всех в команде.
Конфигурационный файл Makefile
Команды для make описываются в файле Makefile (без расширения).
Базовый синтаксис правила выглядит так:
target: <dependencies>
<TAB>command 1
<TAB>command 2target— цель: имя «задачи» (часто это виртуальная команда вродеtest,lint,run).dependencies— зависимости цели (другие цели или файлы).- Команды (recipe) — это обычные shell-команды; каждая строка рецепта должна начинаться с TAB, иначе
makeне распознает её как команду правила.
Пример Makefile
Ниже пример для проекта на uv. Обратите внимание: перед командами именно TAB (не пробелы).
install:
uv sync
run_dev:
uv run app
migrate:
uv run alembic upgrade head
run_prod: install migrate
uv run appВ примере выше у нас четыре команды (цели): install, run_dev, migrate и run_prod. Каждая из них описывает один «шорткат» для частой операции в проекте.
install— выполняет uv sync — установка/обновление зависимостей проекта.run_dev— запускает приложение в режиме разработки командойuv run app.migrate— применяет миграции базы данных черезuv run alembic upgrade head.
Цель с зависимостями
run_prod: install migrate— у целиrun_prodесть зависимостиinstallиmigrate, поэтому приmake run_prodсначала выполняются целиinstallиmigrate(в указанном порядке), а затем командаuv run app. Так можно собрать цепочку действий («установи зависимости → примени миграции → запусти приложение») в одну удобную команду.
Выполнение команды
Команды запускаются просто: make <target>
Для примера выше запуск проекта во время разработки будет таким: make run_dev
Переходим к практике
С теоретической частью покончено — пора переходить к созданию проекта.
Прежде чем начнём, убедитесь, что у вас установлены git, uv и make. Быстро проверить можно так:
git --version
uv --version
make --versionИнициализация проекта
Откройте терминал и перейдите в удобную директорию:
cd <путь_до_директории>Теперь инициализируем проект через uv:
uv init --package ai_moderation_botФлаг --package создаст «пакетный» проект со структурой src/..., а также сгенерирует базовые файлы проекта вроде pyproject.toml и README.md.
Переходим в свежесозданную директорию:
cd ai_moderation_botОтлично, начало положено. Теперь папку проекта можно открыть в любой удобной IDE — я буду использовать PyCharm.

Дорабатываем .gitignore
Как видно на скриншоте, при инициализации проекта uv создаёт .gitignore. Но базовый файл часто неполный: в нём могут отсутствовать правила под IDE, локальную конфигурацию и артефакты вроде логов.

В нашем случае добавим игнорирование:
- настроек IDE (
.idea/,.vscode/), чтобы не тащить в репозиторий личные рабочие файлы (при желании можно игнорировать не всё, а только персональные файлы IDE, но для старта проще целиком). - файла переменных окружения
.env(его обычно не коммитят, потому что там могут оказаться токены/пароли). - директории логов
logs/, потому что это генерируемые файлы.
Откройте .gitignore и добавьте в конец:
# IDE
.idea/
.vscode/
# Config
.env
# Logs
logs/Получается вот так:

Устанавливаем и инициализируем pre-commit
Выполняем то, что было описано ранее.
Сперва установим библиотеку:
uv add pre-commit
Затем, инициализируем:
uv run pre-commit install
Теперь создадим в корне файл .pre-commit-config.yaml со следующим содержимым:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: check-yaml
- id: check-case-conflict
- id: check-merge-conflict
- id: end-of-file-fixer
- repo: https://github.com/asottile/pyupgrade
rev: v3.21.2
hooks:
- id: pyupgrade
args: [ --py313-plus ]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.13.0
hooks:
- id: ruff-check
args: [ "--fix", "--line-length=120" ]
- repo: https://foundry.fsky.io/vel/ty-pre-commit
rev: 0.0.3
hooks:
- id: ty-checkПрежде чем запустить и проверить как работает, необходимо добавить файлы в индекс git-репозитория.
Для этого выполним команду:
git add .Теперь, когда файлы добавлены в отслеживание, запустим pre-commit:
uv run pre-commit run --allСперва идёт скачивание линтеров (хуков). Это происходит только один раз, впоследствии они будут запускаться сразу:

После чего идёт непосредственно проверка:

В двух файлах не было пустой строки в конце и хук end-of-file-fixer это исправил.
Создаём Makefile
В корне создадим файл Makefile. У нас сейчас есть всего одна команда - запуск линтеров, её и добавим.
Создадим цель lint и пропишем команду запуска pre-commit:
lint:
uv run pre-commit run --allТеперь запустим:
make lintВсё работает!

Фиксируем изменения
Остался последний шаг - зафиксировать изменения в git-репозитории.
Для этого выполним команду добавления в git, но уже не всех файлов, а только Makefile, т.к. он единственный новый файл:
git add MakefileТеперь создаём коммит:
git commit -m "Init project"
Заключение
В этой статье было совсем мало практики, но это только первая, отправная точка в большом пути проекта.
В процессе написания мы ещё не раз воспользуемся или нам помогут упомянутые выше инструменты. Не зря они так полюбились разработчикам, что считаются обязательными для каждого проекта.
Подписывайтесь на Telegram-канал «Код на салфетке», и следите за развитием или даже предлагайте свои идеи!
🤫А ещё! У нас скоро стартует новогодний розыгрыш!🤫
Благодарю за прочтение, увидимся в следующих статьях!
Комментарии
Оставить комментарийВойдите, чтобы оставить комментарий.
Комментариев пока нет.