правки для деплоя через Coolify

This commit is contained in:
a.grigorev
2026-09-04 15:14:29 +03:00
parent 161fd6576b
commit 1098ff4991
11 changed files with 276 additions and 384 deletions
+163
View File
@@ -0,0 +1,163 @@
# Бот для отзывов Ozon и Wildberries
Telegram-бот, который собирает новые отзывы с Ozon и Wildberries, публикует их в группу с топиками, анализирует через GPT и помогает на них отвечать: автоматически или по кнопке.
## Что умеет
- **Опрос маркетплейсов.** Фоновые воркеры по таймеру запрашивают новые отзывы через Ozon Seller API и WB Feedbacks API. Каждый отзыв сохраняется в SQLite, повторно не обрабатывается.
- **Раскладка по топикам.** Отзывы с оценкой выше порога (`NEGATIVE_RATING`, по умолчанию 3) уходят в топик хороших, остальные в топик плохих. Отзывы без текста (только оценка) можно направлять в отдельные топики.
- **Анализ через GPT.** Определяет тональность, причину и критичность отзыва по промпту. Критичные отзывы помечаются `#требует_обработки`.
- **Ответы.** Три варианта: автоответ через GPT, автоответ по шаблону для отзывов без текста, либо ручной режим с кнопками «Сгенерировать ответ» / «Написать свой ответ» / «Отправить» / «Удалить ответ».
- **Google Sheets.** Каждый отзыв дописывается строкой в таблицу отзывов. Названия и категории товаров подтягиваются из справочников товаров WB и Ozon.
- **Статистика.** Команды `/stat` (за сегодня) и `/allstat` (за всё время): количество, доля плохих, средний рейтинг, критичные, тональность.
Настройки (промпты, автоответы, модель, шаблоны) хранятся в базе отдельно для хороших и плохих отзывов и меняются прямо из бота. Они сохраняются между перезапусками и редеплоями, поэтому база должна лежать на постоянном томе.
## Как это работает
```
Ozon API ─┐ ┌─> Telegram (топики хороших / плохих / без текста)
├─> воркер ─> SQLite ─> очереди ─┤
WB API ──┘ │ └─> Google Sheets (таблица отзывов)
└─> GPT: анализ + генерация ответа
```
Точка входа `app/__main__.py` поднимает четыре бесконечные задачи: опрос Ozon, опрос WB, отправка сообщений в Telegram, запись в Google Sheets. Затем запускает polling Telegram. Бот реагирует только на сообщения из чата `TG_CHAT_ID` в разрешённых топиках.
Структура кода:
| Путь | Назначение |
|------|------------|
| `app/config.py` | Чтение переменных окружения и `.env`, промпты по умолчанию, шаблоны, настройка ORM |
| `app/handlers/reviews.py` | Формирование сообщения об отзыве, очереди в Telegram и Sheets |
| `app/handlers/callbacks.py` | Кнопки: генерация, отправка, удаление ответа, переключатели настроек |
| `app/handlers/commands.py` | Команды `/stat`, `/allstat`, `/settings`, шаблоны, `/max_tokens` |
| `app/handlers/prompt.py` | Просмотр, изменение и сброс промптов |
| `app/services/ozon.py`, `wb.py` | Клиенты API маркетплейсов и обработка новых отзывов |
| `app/services/gpt.py` | Запросы к GPT, разбор JSON анализа |
| `app/services/sheets.py` | Работа с Google Sheets через gspread |
| `app/services/stats.py` | Подсчёт статистики |
| `app/models/` | Модели `Review` и `Settings` (Tortoise ORM) |
## Требования
- Python 3.12+
- [uv](https://docs.astral.sh/uv/) (рекомендуется) или pip
- Telegram-бот, добавленный в супергруппу с включёнными топиками
- Сервисный аккаунт Google с доступом к Google Sheets API
- Ключи API Ozon Seller, Wildberries (категория «Отзывы и вопросы») и gptunnel.ru
## Установка и запуск
1. Клонируйте репозиторий и установите зависимости:
```bash
uv sync
```
Без uv: `python -m venv .venv`, активировать окружение, затем `pip install -e .`
2. Создайте `.env` из шаблона и заполните значения:
```bash
cp .env.example .env
```
Описание всех переменных есть внутри `.env.example`. Минимум для запуска: `BOT_TOKEN`, `TG_CHAT_ID`, `GOOD_REVIEWS_THREAD_ID`, `BAD_REVIEWS_THREAD_ID`.
3. Подключите ключ сервисного аккаунта Google одним из способов:
- положите файл в `google-auth/key.json` (путь можно поменять через `GOOGLE_SERVICE_ACCOUNT_FILE`);
- или вставьте содержимое JSON-ключа в переменную `GOOGLE_SERVICE_ACCOUNT_JSON`. Переменная имеет приоритет над файлом.
Без ключа бот запустится, но не сможет ни читать справочники товаров, ни писать отзывы в таблицу, поэтому обработка отзывов работать не будет.
Как получить ключ: в [Google Cloud Console](https://console.cloud.google.com/) создайте проект, включите Google Sheets API, создайте сервисный аккаунт и скачайте JSON-ключ. Затем откройте доступ на редактирование ко всем нужным таблицам для email из поля `client_email` этого ключа.
4. Запустите бота:
```bash
uv run python -m app
```
Запускать нужно из корня проекта, потому что пути к `.env` и `google-auth/key.json` относительные.
## Деплой в Coolify
Бот рассчитан на запуск из Coolify без каких-либо файлов на сервере: все настройки передаются через переменные окружения.
1. **Создайте ресурс.** В проекте Coolify нажмите «New Resource», выберите репозиторий (публичный по ссылке или через GitHub App), ветку и build pack **Dockerfile**. Порт и домен оставьте пустыми: бот не поднимает HTTP-сервер. Если Coolify предлагает health check, отключите его.
2. **Заполните переменные окружения.** На вкладке «Environment Variables» добавьте переменные из [.env.example](.env.example). Минимальный набор для рабочего инстанса:
| Переменная | Значение |
|------------|----------|
| `BOT_TOKEN` | Токен бота от @BotFather |
| `TG_CHAT_ID`, `GOOD_REVIEWS_THREAD_ID`, `BAD_REVIEWS_THREAD_ID` | Группа и топики |
| `GOOGLE_SERVICE_ACCOUNT_JSON` | Содержимое `key.json` одной строкой или в base64 |
| `REVIEWS_SHEET_LINK`, `PRODUCTS_WB_SHEET_LINK`, `PRODUCTS_OZON_SHEET_LINK` | Ссылки на таблицы |
| `OZON_API_KEY`, `OZON_CLIENT_ID`, `WB_API_KEY`, `GPTUNNEL_API_KEY` | Ключи API |
| `BRAND_NAME` | Название бренда для промптов |
Ключ Google удобнее передавать в base64, тогда в значении нет кавычек и переносов строк:
```bash
base64 -w0 key.json
```
Бот сам определит формат: если значение начинается с `{`, оно читается как JSON, иначе декодируется из base64.
3. **Подключите постоянное хранилище.** На вкладке «Storages» добавьте volume с точкой монтирования `/data`. Там лежит `db.sqlite3` с историей отзывов и настройками. Без тома база будет теряться при каждом редеплое, и бот заново отправит в Telegram последние отзывы с маркетплейсов.
4. **Нажмите Deploy.** Образ соберётся по `Dockerfile` с зависимостями из `uv.lock`. В логах контейнера должны появиться строки `Start polling` и `Checking for new ... reviews`.
**Несколько инстансов.** Для каждого бренда или магазина создайте отдельный ресурс из того же репозитория со своим набором переменных и своим volume. Инстансы не зависят друг от друга. Можно указать одну и ту же ветку, тогда все они обновятся при пуше, если включён автодеплой.
## Запуск в Docker вручную
```bash
docker compose up -d --build
```
Compose читает `.env` из корня, если файл есть, и хранит базу в томе `bot-data`. Ключ Google передаётся через `GOOGLE_SERVICE_ACCOUNT_JSON` либо монтированием `google-auth/` (закомментировано в `docker-compose.yml`). Логи: `docker compose logs -f bot`. Остановка: `docker compose down` (с флагом `-v` удалится и база).
## Настройка Telegram
- Узнать ID чата: добавьте в группу бота [@userinfobot](https://t.me/userinfobot) или посмотрите в ссылке на сообщение.
- ID топика: число в конце ссылки на топик `https://t.me/c/<chat>/<topic>`.
- Все команды бота работают только внутри разрешённых топиков. Настройки применяются к тому типу отзывов, в чьём топике вызвана команда (хорошие или плохие).
## Google-таблицы
**Таблица отзывов** (`REVIEWS_SHEET_LINK`). Первая строка должна содержать 12 заголовков в таком порядке:
`Дата | Площадка | Название товара | Категория | Подкатегория | ID товара | Оценка | Текст отзыва | Тональность | Причина | Критичность | Тэги`
**Справочник товаров WB** (`PRODUCTS_WB_SHEET_LINK`). Ключевой столбец `SKU` или `Артикул WB`. Используются столбцы `Название`/`Наименование` и `Категория`/`Категория продавца`.
**Справочник товаров Ozon** (`PRODUCTS_OZON_SHEET_LINK`). Ключевой столбец `Ozon ID` или `ozon id акутальный`. Используются `Товары`, `Тип товара`, `Категория 2-го уровня`.
Если в ссылке на таблицу есть `gid=`, берётся этот лист, иначе первый. Справочники кэшируются на 5 минут.
## Команды бота
| Команда | Действие |
|---------|----------|
| `/settings` | Панель настроек с переключателями: автоответ с текстом, автоответ без текста, анализ, модель GPT |
| `/stat`, `/allstat` | Статистика за сегодня / за всё время |
| `/show_prompt` | Показать текущие промпты анализа и ответа |
| `/change_prompt`, `/reset_prompt` | Изменить / сбросить промпт анализа |
| `/change_answer_prompt`, `/reset_answer_prompt` | Изменить / сбросить промпт ответа |
| `/template_low текст`, `/template_high текст` | Шаблоны ответа на отзывы без текста (1–3 и 4–5 звёзд) |
| `/show_templates` | Показать текущие шаблоны |
| `/max_tokens N` | Лимит токенов для ответа GPT |
| `/cancel` | Отменить ввод промпта или ответа |
Промпты по умолчанию заданы в `app/config.py`, название бренда в них подставляется из `BRAND_NAME`. Промпт копируется в базу при первом запуске, поэтому смена `BRAND_NAME` на уже работающем инстансе не изменит сохранённый промпт: сбросьте его командами `/reset_prompt` и `/reset_answer_prompt`.
## Данные
База SQLite (`db.sqlite3`) создаётся автоматически при первом запуске. Таблицы `review` и `settings` создаются через `generate_schemas`, недостающие колонки добавляются простой миграцией при старте.
## Что не должно попадать в git
Файлы `.env`, `google-auth/key.json` и `*.sqlite3` уже исключены через `.gitignore`. Если ключ когда-либо был закоммичен, его нужно отозвать и выпустить заново, удаления из истории недостаточно.