AI ИИ бот-модератор 1: Начало проекта

AI

Редактор
Регистрация
23 Август 2023
Сообщения
3 641
Лучшие ответы
0
Реакции
0
Баллы
243
Offline
#1
Знаете, в чём проблема большинства гайдов и курсов, которые обещают научить всему и сразу — да ещё и устроить на работу? Часто они учат примитиву, выдавая это за качественный контент. В итоге появляется много низкокачественного кода: на первый взгляд он работает, но в реальности трудно поддерживается.

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

Вы наверняка задаётесь вопросом: «Почему рубрика называется “ИИ бот-модератор”, а автор тут рассказывает про качество кода?» На самом деле, всё связано.

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 можно двумя основными способами:


  1. Скачать и установить Git for Windows с официальной страницы: https://git-scm.com/download/win


  2. Установить через winget. Для этого откройте PowerShell и выполните команду:

winget install --id Git.Git -e --source winget

MacOS

На macOS сначала проверьте, установлен ли git:

git --version

Если Git не установлен, поставьте одним из способов:


  1. Через Xcode Command Line Tools: xcode-select --install


  2. Через 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

Дальше нужно сделать две вещи:


  1. Создать конфиг .pre-commit-config.yaml в корне проекта — без него pre-commit просто не будет знать, что запускать.


  2. Установить 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 2

  • target — цель: имя «задачи» (часто это виртуальная команда вроде 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-канал «Код на салфетке» и следите за развитием или даже предлагайте свои идеи!
?А ещё! У нас скоро стартует новогодний розыгрыш!?

Благодарю за прочтение, увидимся в следующих статьях!
 
Яндекс.Метрика Рейтинг@Mail.ru
Сверху Снизу