# Бот для отзывов 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//`. - Все команды бота работают только внутри разрешённых топиков. Настройки применяются к тому типу отзывов, в чьём топике вызвана команда (хорошие или плохие). ## 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`. Если ключ когда-либо был закоммичен, его нужно отозвать и выпустить заново, удаления из истории недостаточно.