refactor: review fixes and code improvements
- Add shared proxy module (bot/utils/proxy.py) to eliminate duplicate SOCKS5 parsing - Fix AI client: escape HTML before Markdown→HTML conversion, unify timeouts, make health check optional and disabled by default, handle 429 retries - Fix ai.py: correct forwarded message handling (aiogram 3.x forward_origin), pass relevant summaries via extra_system_content - Fix dialogue.py: only respond to Astra's messages, use system context instead of prompt injection, answer on the limit message before phase transition - Fix voice.py: load Whisper model in thread pool, safe WAV path generation - Improve database.py: composite indexes, Boolean is_active, upsert file_id cache, add context cleanup helper - Update weather.py and yadisk_download.py to use shared proxy connector - Update yadisk.py: validate URL before cache clear, add download size limit, wrap sync file ops in to_thread - Reuse S3 client via lru_cache - Update setup_commands with /aiclear and /aiuser - Update README, Dockerfile (Python 3.11), docker-compose (mount models) - Pin dependency versions, remove unused httpx[socks] - Add basic pytest tests for layout converter and voice normalization
This commit is contained in:
@@ -7,7 +7,11 @@
|
||||
- **Модерация стикеров и GIF** — если пользователь отправляет 25+ стикеров/GIF за 60 секунд, бот удаляет все последующие стикеры и GIF от этого пользователя в течение 5 минут. Текстовые сообщения не затрагиваются.
|
||||
- **Восстановление раскладки (`/res`)** — ответьте командой `/res` на сообщение с текстом в неправильной раскладке, и бот переведёт его (например, `gbdj` → `пиво`).
|
||||
- **Погода (`/weather`)** — покажет подробную погоду в указанном городе через OpenWeatherMap API.
|
||||
- **AI-ассистент "Астра" (`/ai`)** — перешлите сообщение или ответьте на него командой `/ai`, и Astra проанализирует его с учётом контекста последних 10 сообщений.
|
||||
- **AI-ассистент "Астра" (`/ai`)** — задайте вопрос, ответьте командой `/ai` на сообщение или перешлите сообщение, и Astra проанализирует его с учётом контекста и ранее сохранённых выжимок диалогов.
|
||||
- **Диалог с Астрой** — после `/ai` без вопроса начинается диалог. Отвечайте на сообщения бота, чтобы продолжать.
|
||||
- Фаза 1: до 50 сообщений.
|
||||
- Фаза 2: дополнительно до 20 сообщений.
|
||||
- После исчерпания лимита — перерыв 1 час.
|
||||
- **Управление AI (`/aino`, `/aiyes`)** — владелец чата может заблокировать/разблокировать пользователя от AI.
|
||||
- **Поддержка SOCKS5 прокси** — настраивается через `.env`.
|
||||
|
||||
@@ -20,24 +24,33 @@ umb/
|
||||
├── requirements.txt
|
||||
├── main.py # точка входа
|
||||
├── config.py # конфигурация
|
||||
├── Dockerfile
|
||||
├── docker-compose.yml
|
||||
└── bot/
|
||||
├── __init__.py
|
||||
├── bot.py # инициализация бота и сессии
|
||||
├── setup_commands.py # меню команд
|
||||
├── routers/
|
||||
│ ├── __init__.py
|
||||
│ ├── moderation.py # роутер модерации стикеров/GIF
|
||||
│ ├── layout.py # роутер /start, /res
|
||||
│ ├── weather.py # роутер /weather
|
||||
│ └── ai.py # роутер /ai, /aino, /aiyes
|
||||
├── utils/
|
||||
│ ├── __init__.py
|
||||
│ ├── layout_converter.py # конвертер раскладки en→ru
|
||||
│ ├── weather.py # функция получения погоды
|
||||
│ ├── ai_client.py # OpenRouter AI клиент
|
||||
│ ├── database.py # SQLAlchemy модели и функции БД
|
||||
│ └── moderation.py # логика модерации (устарел, теперь в database.py)
|
||||
└── data/
|
||||
└── umb.db # SQLite база данных
|
||||
│ ├── ai.py # роутер /ai, /aino, /aiyes, /aiclear, /aiuser
|
||||
│ ├── dialogue.py # диалоговый режим с Астрой
|
||||
│ ├── voice.py # распознавание голосовых сообщений
|
||||
│ └── yadisk.py # скачивание с Яндекс.Диска
|
||||
└── utils/
|
||||
├── __init__.py
|
||||
├── proxy.py # общий SOCKS5-коннектор
|
||||
├── layout_converter.py # конвертер раскладки en→ru
|
||||
├── weather.py # функция получения погоды
|
||||
├── ai_client.py # OpenRouter/RouterAI клиент
|
||||
├── memory.py # саммари и эмбеддинги
|
||||
├── database.py # SQLAlchemy модели и функции БД
|
||||
├── voice.py # загрузка и транскрибация голоса
|
||||
├── yadisk_download.py # загрузка файлов с Яндекс.Диска
|
||||
├── s3_client.py # загрузка больших файлов в S3
|
||||
└── logging_config.py # настройка логирования
|
||||
```
|
||||
|
||||
## Установка
|
||||
@@ -61,6 +74,11 @@ API_WEATHER=your_openweather_api_key
|
||||
OPENROUTER_API_KEY=your_openrouter_api_key
|
||||
ROUTERAI_API_KEY=your_routerai_api_key
|
||||
ROUTERAI_BASE_URL=https://routerai.ru/api/v1
|
||||
|
||||
# Опционально: периодическая проверка работоспособности бесплатных моделей.
|
||||
# Внимание: проверка расходует токены API. По умолчанию отключена.
|
||||
AI_HEALTH_CHECK_ENABLED=false
|
||||
AI_HEALTH_CHECK_INTERVAL=600
|
||||
```
|
||||
|
||||
Для включения прокси установите `PROXY_ENABLED=true` и укажите корректный `PROXY_URL`.
|
||||
@@ -72,6 +90,14 @@ source .venv/bin/activate
|
||||
python main.py
|
||||
```
|
||||
|
||||
## Запуск в Docker
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
Модели Whisper сохраняются в `./models`, база данных и логи — в `./bot/data`.
|
||||
|
||||
## Команды
|
||||
|
||||
| Команда | Описание |
|
||||
@@ -79,9 +105,15 @@ python main.py
|
||||
| `/start` | Приветствие и информация о боте |
|
||||
| `/res` | Восстановить раскладку (ответить на сообщение) |
|
||||
| `/weather <город>` | Погода в указанном городе |
|
||||
| `/ai` | AI-анализ пересланного/ответного сообщения |
|
||||
| `/ai <вопрос>` | Задать вопрос Астре |
|
||||
| `/ai` (ответом/пересылкой) | Проанализировать сообщение |
|
||||
| `/ai` (без вопроса) | Начать диалоговый режим |
|
||||
| `/aiclear` | Очистить диалог и сохранить выжимку |
|
||||
| `/aiuser` | Список пользователей чата (только creator) |
|
||||
| `/aino @username` | Заблокировать пользователя от AI (только creator, 24ч) |
|
||||
| `/aiyes @username` | Разблокировать пользователя от AI (только creator) |
|
||||
| `/ydf <ссылка>` | Скачать файл с Яндекс.Диска |
|
||||
| `/ydf (new) <ссылка>` | Скачать файл, игнорируя кеш Telegram |
|
||||
|
||||
## Настройки модерации
|
||||
|
||||
@@ -93,12 +125,9 @@ python main.py
|
||||
|
||||
## AI-ассистент "Астра"
|
||||
|
||||
- **Бесплатные модели** (OpenRouter): пробует по очереди все, при rate-limit переключается на следующую
|
||||
- `deepseek/deepseek-v4-flash:free`
|
||||
- `google/gemma-4-26b-a4b-it:free`
|
||||
- `minimax/minimax-m2.5:free`
|
||||
- `qwen/qwen3-next-80b-a3b-instruct:free`
|
||||
- **Платный fallback** (RouterAI): если все бесплатные модели вернули 429, используется `deepseek/deepseek-v4-flash` через `routerai.ru`. В ответе добавляется уведомление `⚡ Обработано через платный API`.
|
||||
- **Контекст**: хранит последние 10 сообщений каждого пользователя в SQLite
|
||||
- **Стиль**: отвечает кратко и по делу, если не просят развёрнуто
|
||||
- **Бесплатные модели** (OpenRouter): пробует по очереди доступные модели с суффиксом `:free`. При rate-limit автоматически переключается на следующую.
|
||||
- **Платный fallback** (RouterAI): если все бесплатные модели недоступны, используется `deepseek/deepseek-v4-flash` через `routerai.ru`. В ответе добавляется уведомление `⚡ Обработано через платный API`.
|
||||
- **Контекст**: хранит последние 10 сообщений каждого пользователя в SQLite.
|
||||
- **Долгосрочная память**: при переходе между фазами диалога и при `/aiclear` создаётся краткая выжимка, которая затем подбирается по смыслу к новым вопросам.
|
||||
- **Стиль**: отвечает кратко и по делу, если не просят развёрнуто.
|
||||
- **Потеря контекста**: говорит что-то милое ("я потеряла мысль", "смотри, какая птичка!")
|
||||
|
||||
Reference in New Issue
Block a user