Files
mp-answer-bot/README.md
T

164 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Бот для отзывов 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`. Если ключ когда-либо был закоммичен, его нужно отозвать и выпустить заново, удаления из истории недостаточно.