Files
AnonBot/README.md

1866 lines
83 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.
# 🤖 AnonBot - Telegram бот для анонимных вопросов
Telegram-бот для приема и обработки анонимных вопросов с использованием aiogram 3.x.
## ✨ Возможности
- 🔗 **Персональные ссылки** - каждый пользователь получает уникальную ссылку для приема вопросов
- 👤 **Анонимность** - вопросы отправляются анонимно, личность отправителя скрыта
- 💬 **Интерактивные ответы** - удобный интерфейс для ответов на вопросы через inline кнопки
- 🔢 **Локальная нумерация** - каждый пользователь видит свои вопросы с номерами #1, #2, #3... вместо глобальных ID
- 📊 **Статистика** - подробная статистика для администраторов
- 🗄️ **База данных** - SQLite для хранения пользователей и вопросов с автоматической нумерацией
- 👑 **Админ панель** - управление ботом для администраторов
- 🔍 **Суперпользователи** - расширенные права для модерации с отображением информации об авторах вопросов
- 🏗️ **Современная архитектура** - система инъекции зависимостей для лучшей тестируемости
- 📝 **Продвинутое логирование** - автоматические декораторы, контекстное логирование, FSM отслеживание
- 🛡️ **Безопасность** - валидация данных, обработка ошибок, система ролей
- 🔍 **Централизованная валидация** - автоматическая валидация всех входных данных с санитизацией
- 🔐 **Система разрешений** - гибкая система разрешений с соблюдением принципа OCP
- 📈 **Prometheus метрики** - полный мониторинг производительности и состояния бота
## 🚀 Быстрый старт
### 1. Установка зависимостей
```bash
pip install -r requirements.txt
pip install dependency-injector # Для системы инъекции зависимостей
```
### 2. Настройка конфигурации
Создайте файл `.env` в корневой директории проекта:
```env
# Токен бота от @BotFather
BOT_TOKEN=your_bot_token_here
# ID администраторов через запятую (можно получить у @userinfobot)
ADMINS=123456789,987654321
# Путь к базе данных SQLite
DATABASE_PATH=database/anon_qna.db
# Режим отладки (true/false)
DEBUG=false
# Максимальная длина вопроса
MAX_QUESTION_LENGTH=1000
# Максимальная длина ответа
MAX_ANSWER_LENGTH=2000
```
### 3. Запуск бота
#### Локальный запуск
```bash
python main.py
```
или
```bash
python bot.py
```
#### Запуск в Docker
1. Соберите Docker образ:
```bash
docker build -t anon-bot .
```
2. Запустите контейнер:
```bash
docker run -d \
--name anon-bot \
--restart unless-stopped \
-p 8081:8081 \
-v $(pwd)/database:/app/database \
-v $(pwd)/logs:/app/logs \
-e BOT_TOKEN=your_bot_token_here \
-e ADMINS=123456789,987654321 \
-e DEBUG=false \
anon-bot
```
3. Проверьте статус:
```bash
docker logs anon-bot
```
4. Проверьте метрики:
```bash
curl http://localhost:8081/health
```
## 📁 Структура проекта
```
AnonBot/
├── main.py # Точка входа
├── bot.py # Основной файл бота
├── loader.py # Инициализация бота
├── dependencies.py # Система инъекции зависимостей
├── utils.py # Общие утилиты
├── requirements.txt # Зависимости
├── README.md # Документация
├── Dockerfile # Docker образ
├── .dockerignore # Исключения для Docker
├── .env_example # Пример переменных окружения
├── prometheus.yml # Конфигурация Prometheus
├── config/ # Конфигурация
│ ├── __init__.py
│ ├── config.py # Основная конфигурация
│ └── constants.py # Константы приложения
├── handlers/ # Обработчики сообщений
│ ├── __init__.py
│ ├── start.py # Команды /start, /help
│ ├── questions.py # Обработка анонимных вопросов
│ ├── answers.py # Обработка ответов
│ ├── admin.py # Админ функции
│ └── errors.py # Глобальная обработка ошибок
├── keyboards/ # Клавиатуры
│ ├── __init__.py
│ ├── inline.py # Inline клавиатуры
│ └── reply.py # Reply клавиатуры
├── models/ # Модели данных
│ ├── __init__.py
│ ├── user.py # Модель пользователя
│ ├── question.py # Модель вопроса
│ ├── user_block.py # Модель блокировки
│ └── user_settings.py # Модель настроек
├── services/ # Сервисы (реорганизованы по категориям)
│ ├── __init__.py
│ ├── utils.py # Общие утилиты
│ ├── auth/ # Авторизация и разрешения
│ │ ├── __init__.py
│ │ └── auth_new.py # Сервис авторизации с системой разрешений
│ ├── validation/ # Валидация входных данных
│ │ ├── __init__.py
│ │ └── input_validator.py # Централизованный валидатор
│ ├── business/ # Бизнес-логика
│ │ ├── __init__.py
│ │ ├── user_service.py # Сервис пользователей
│ │ ├── question_service.py # Сервис вопросов
│ │ ├── message_service.py # Сервис сообщений
│ │ └── pagination_service.py # Сервис пагинации
│ ├── infrastructure/ # Инфраструктурные сервисы
│ │ ├── __init__.py
│ │ ├── database.py # Работа с БД
│ │ ├── logger.py # Система логирования
│ │ ├── logging_decorators.py # Декораторы для автоматического логирования
│ │ ├── logging_utils.py # Утилиты для контекстного логирования
│ │ ├── metrics.py # Prometheus метрики
│ │ ├── http_server.py # HTTP сервер для метрик
│ ├── rate_limiting/ # Rate limiting
│ │ ├── __init__.py
│ │ ├── rate_limit_config.py # Конфигурация rate limiting
│ │ ├── rate_limiter.py # Основной rate limiter
│ │ └── rate_limit_service.py # Сервис rate limiting
│ └── permissions/ # Система разрешений
│ ├── __init__.py
│ ├── base.py # Базовые классы
│ ├── registry.py # Реестр разрешений
│ ├── permissions.py # Стандартные разрешения
│ ├── decorators.py # Декораторы для проверки
│ └── init_permissions.py # Инициализация
├── examples/ # Примеры использования
│ └── dependency_injection_example.py
├── database/ # База данных
│ ├── __init__.py
│ ├── schema.sql # Схема БД
│ ├── crud.py # CRUD операции
│ └── examples.py # Примеры использования
├── middlewares/ # Middleware
│ ├── __init__.py
│ ├── rate_limit_middleware.py # Middleware для rate limiting
│ └── validation_middleware.py # Middleware для валидации данных
├── docs/ # Документация
└── logs/ # Логи приложения
```
### 🏗️ Архитектурные улучшения
Проект был реорганизован для улучшения структуры и масштабируемости:
#### 📁 Новая структура services
**До реорганизации:**
- Все сервисы в корне `services/`
- Смешанные категории (бизнес-логика + инфраструктура)
- Дублирование функциональности
**После реорганизации:**
- **`services/auth/`** - авторизация и разрешения
- **`services/business/`** - бизнес-логика (пользователи, вопросы, сообщения)
- **`services/infrastructure/`** - инфраструктурные сервисы (БД, логи, метрики)
- **`services/rate_limiting/`** - rate limiting компоненты
- **`services/permissions/`** - система разрешений (без изменений)
#### 📁 Новая структура config
**До реорганизации:**
- `config.py` и `constants.py` в корне проекта
**После реорганизации:**
- **`config/`** - папка для всех конфигурационных файлов
- **`config/config.py`** - основная конфигурация
- **`config/constants.py`** - константы приложения
#### ✅ Преимущества новой структуры
1. **Логическая группировка** - связанные сервисы в одних папках
2. **Разделение ответственности** - бизнес-логика отдельно от инфраструктуры
3. **Масштабируемость** - легко добавлять новые сервисы в нужные категории
4. **Читаемость** - понятно, где что находится
5. **Обратная совместимость** - старые импорты продолжают работать
#### 🔄 Обратная совместимость
Все импорты обновлены, но старые импорты продолжают работать благодаря `__init__.py` файлам:
```python
# Старый способ (все еще работает)
from services.database import DatabaseService
from config import config
# Новый способ (рекомендуется)
from services.infrastructure.database import DatabaseService
from config import config
```
### 🔍 Система валидации входных данных
Реализована централизованная система валидации всех входящих данных для обеспечения безопасности и стабильности:
#### 📋 Что валидируется
- **Telegram ID** - проверка диапазона и типа данных
- **Username** - валидация формата и допустимых символов
- **Текстовый контент** - проверка длины, HTML санитизация, защита от спама
- **Deep links** - валидация формата и длины
- **Callback data** - проверка безопасности и формата
- **Параметры пагинации** - валидация диапазонов
#### 🛡️ Безопасность
- **HTML санитизация** - автоматическое экранирование опасных тегов
- **Защита от спама** - проверка на повторяющиеся символы и слова
- **Валидация ID** - проверка корректности всех идентификаторов
- **Логирование** - полное логирование всех ошибок валидации с автоматическими декораторами
#### 🏗️ Архитектура валидации
```python
# Централизованный валидатор
from services.validation import InputValidator
validator = InputValidator()
# Валидация текста вопроса
result = validator.validate_question_text(text, max_length=1000)
if not result:
print(f"Ошибка: {result.error_message}")
else:
sanitized_text = result.sanitized_value
# Валидация callback data
result = validator.validate_callback_data(callback_data)
```
#### 🔄 Middleware валидации
Автоматическая валидация всех входящих данных через middleware:
```python
# ValidationMiddleware автоматически валидирует:
# - Все callback queries
# - Все сообщения
# - Telegram ID пользователей
# - Username (если есть)
# - Chat ID
```
#### ✅ Преимущества
1. **Безопасность** - защита от некорректных данных и атак
2. **Стабильность** - предотвращение ошибок от невалидных данных
3. **UX** - понятные сообщения об ошибках для пользователей
4. **Мониторинг** - полное логирование проблем валидации
5. **Централизация** - единая точка валидации для всего приложения
## 🎯 Как это работает
### Для пользователей:
1. **Регистрация**: Пользователь запускает бота командой `/start`
2. **Получение ссылки**: Бот генерирует персональную ссылку формата `t.me/bot_username?start=ref_{anonymous_id}`
3. **Публикация ссылки**: Пользователь делится ссылкой в социальных сетях
4. **Получение вопросов**: Друзья переходят по ссылке и задают анонимные вопросы
5. **Ответы**: Пользователь получает уведомления и может отвечать на вопросы
### Для отправителей вопросов:
1. **Переход по ссылке**: Отправитель переходит по персональной ссылке пользователя
2. **Задание вопроса**: Отправляет вопрос боту
3. **Анонимность**: Вопрос передается получателю анонимно
4. **Получение ответа**: Если получатель ответит, ответ будет показан
## 🔧 Команды бота
### Основные команды:
- `/start` - Запуск бота и получение персональной ссылки
- `/help` - Справка по использованию бота
### Админ команды:
- `/stats` - Показать статистику бота (только для админов)
## 👑 Админ панель
Администраторы имеют доступ к дополнительным функциям:
- 📊 **Статистика** - общая статистика бота, пользователей и вопросов
- 👥 **Пользователи** - список всех пользователей бота
-**Все вопросы** - статистика по всем вопросам
- 📢 **Рассылка** - функция рассылки (планируется)
- ⚙️ **Настройки** - просмотр текущих настроек бота
## 🔍 Система ролей и суперпользователи
Бот поддерживает трехуровневую систему ролей:
### 👑 Администраторы (admin)
- Определяются в конфигурации (`ADMINS` в `.env`)
- Имеют все права доступа
- Не могут быть изменены через базу данных
### 🔍 Суперпользователи (superuser)
- Определяются в базе данных (поле `is_superuser = TRUE`)
- Имеют расширенные права для модерации
- **Могут видеть информацию об авторах вопросов**
#### Особенности для суперпользователей:
**Отображение списка вопросов:**
```
10. ✅ #2 Вопрос от @username FirstName LastName
```
**Уведомления о новых вопросах:**
```
❓ Новый вопрос от @username FirstName LastName!
📝 Вопрос:
Текст вопроса...
📅 05.09.2025 23:27
```
### 👤 Обычные пользователи (user)
- Стандартные права доступа
- Видят анонимные вопросы без информации об авторах
### Назначение суперпользователя
Суперпользователей можно назначать через базу данных:
```sql
UPDATE users SET is_superuser = TRUE WHERE telegram_id = 123456789;
```
Или программно:
```python
from services.infrastructure.database import DatabaseService
async def make_superuser(telegram_id: int):
db_service = DatabaseService("database/anon_qna.db")
user = await db_service.get_user(telegram_id)
if user:
user.is_superuser = True
await db_service.update_user(user)
```
## 🔐 Система разрешений
AnonBot использует современную систему разрешений, построенную с соблюдением принципа открытости/закрытости (OCP):
### ✅ Преимущества новой системы
- **Открытость для расширения** - новые разрешения добавляются без изменения существующего кода
- **Закрытость для модификации** - существующий код не изменяется при добавлении новых разрешений
- **Типобезопасность** - использование классов вместо строк
- **Единая точка входа** - все проверки разрешений через один интерфейс
- **Удобные декораторы** - простое применение проверок разрешений
### 🎯 Стандартные разрешения
| Разрешение | Описание | Доступ |
|------------|----------|--------|
| `admin` | Права администратора | Только администраторы |
| `superuser` | Права суперпользователя | Только суперпользователи |
| `view_stats` | Просмотр статистики | Администраторы + суперпользователи |
| `admin_panel` | Доступ к админ панели | Администраторы + суперпользователи |
| `manage_users` | Управление пользователями | Администраторы + суперпользователи |
| `broadcast` | Рассылка сообщений | Только администраторы |
| `view_questions` | Просмотр вопросов | Все активные пользователи |
| `ask_questions` | Задавание вопросов | Все активные незабаненные пользователи |
| `answer_questions` | Ответы на вопросы | Все активные незабаненные пользователи |
### 🚀 Использование
#### С декораторами (рекомендуется)
```python
from services.permissions.decorators import require_permission
@router.message(Command("my_command"))
@require_permission("view_stats", "У вас нет прав для выполнения этой команды.")
async def my_command_handler(message: Message):
# Логика обработчика
await message.answer("Команда выполнена!")
```
#### Готовые декораторы
```python
@require_permission("view_stats") # Конкретное разрешение
@require_admin() # Только администраторы
@require_superuser() # Только суперпользователи
@require_admin_or_superuser() # Администраторы или суперпользователи
@require_active_user() # Активные пользователи
@require_unbanned_user() # Незабаненные пользователи
```
#### Добавление нового разрешения
```python
# 1. Создаем класс разрешения
class MyCustomPermission(Permission):
async def check(self, user_id: int, database: DatabaseService, config) -> bool:
return user_id in config.ADMINS
# 2. Регистрируем разрешение
register_permission(MyCustomPermission())
# 3. Используем в обработчике
@require_permission("my_custom_permission")
async def my_handler(message: Message):
# Логика обработчика
pass
```
## 🛡️ Безопасность
- **Валидация**: Проверка всех входящих данных
- **Обработка ошибок**: Глобальная обработка ошибок с уведомлениями админов
- **Логирование**: Автоматическое логирование с декораторами, контекстная информация, FSM отслеживание
- **Система ролей**: Трехуровневая система доступа (админ/суперпользователь/пользователь)
- **Система разрешений**: Гибкая система разрешений с соблюдением принципа OCP
## 🏗️ Система инъекции зависимостей
AnonBot использует современную систему инъекции зависимостей, построенную на основе **MagicData** из aiogram 3.x. Это обеспечивает:
- **Тестируемость**: Легко мокать зависимости в тестах
- **Читаемость**: Явные зависимости в сигнатурах функций
- **Гибкость**: Легко заменять реализации сервисов
- **Обратная совместимость**: Старый код продолжает работать
### Доступные сервисы
1. **DatabaseService** - работа с базой данных, CRUD операции
2. **AuthService** - авторизация, проверка прав, управление ролями
3. **InputValidator** - централизованная валидация всех входных данных
4. **UtilsService** - форматирование данных, утилиты
5. **RateLimitService** - управление rate limiting, статистика
6. **Config** - доступ к конфигурации приложения
### Быстрый старт с DI
```python
# Подключение в loader.py
from dependencies import DependencyMiddleware, get_dependencies
async def init_dispatcher() -> Dispatcher:
dp = Dispatcher(storage=storage)
# Инициализируем зависимости
deps = get_dependencies()
await deps.init()
# Добавляем middleware
dp.update.middleware(DependencyMiddleware(deps))
return dp
# Использование в обработчиках
from dependencies import inject_start_services, inject_question_services, inject_answer_services
from services.infrastructure.database import DatabaseService
from services.auth.auth_new import AuthService
from services.validation import InputValidator
@router.message(Command("start"))
@inject_start_services
async def cmd_start(
message: Message,
user_service: UserService,
auth: AuthService,
utils: UtilsService,
message_service: MessageService,
validator: InputValidator
):
# Валидируем входные данные
user_id_validation = validator.validate_telegram_id(message.from_user.id)
if not user_id_validation:
await message.answer("❌ Ошибка: недопустимый ID пользователя")
return
# Используем сервисы без хардкода
is_admin = auth.is_admin(message.from_user.id)
user = await user_service.get_user_by_telegram_id(message.from_user.id)
```
### Способы инъекции
1. **Специализированные декораторы** (рекомендуется):
- `@inject_question_services` - для обработки вопросов
- `@inject_answer_services` - для обработки ответов
- `@inject_start_services` - для команды /start
- `@inject_link_services` - для кнопки "Моя ссылка"
- `@inject_main_menu_services` - для кнопки "Главное меню"
2. **Базовые декораторы**:
- `@inject_database` - только DatabaseService
- `@inject_auth` - только AuthService
- `@inject_utils` - только UtilsService
3. **Автоматически**: aiogram инжектит зависимости из middleware
4. **Обратная совместимость**: старые функции продолжают работать
### Примеры использования специализированных декораторов
#### Обработка вопросов
```python
@router.message(StateFilter(QuestionStates.waiting_for_question))
@inject_question_services
async def process_anonymous_question(
message: Message,
state: FSMContext,
question_service: QuestionService,
user_service: UserService,
message_service: MessageService,
validator: InputValidator
):
# Валидируем и обрабатываем вопрос
validation_result = validator.validate_question_text(message.text)
if not validation_result:
await message_service.send_message(message, "❌ Неверный формат вопроса")
return
# Создаем вопрос через сервис
question = await question_service.create_question(
message.from_user.id,
target_user_id,
validation_result.sanitized_value
)
```
#### Обработка ответов
```python
@router.message(StateFilter(AnswerStates.waiting_for_answer))
@inject_answer_services
async def process_new_answer(
message: Message,
state: FSMContext,
validator: InputValidator
):
# Валидируем ответ
validation_result = validator.validate_answer_text(message.text)
if not validation_result:
await message.answer("❌ Неверный формат ответа")
return
# Сохраняем ответ
# ... логика сохранения
```
### Локальная нумерация вопросов
#### Отображение вопросов с локальными номерами
```python
# В модели Question добавлен метод get_display_number()
def get_display_number(self) -> int:
"""Получить номер вопроса для отображения (приоритет user_question_number)"""
return self.user_question_number if self.user_question_number is not None else self.id
# В обработчиках используется локальная нумерация
questions_text += f"{i}. {emoji} <b>#{question.get_display_number()}</b>"
```
#### Автоматическая нумерация через триггеры БД
```sql
-- Триггер для автоматического вычисления номера при создании
CREATE TRIGGER calculate_user_question_number
AFTER INSERT ON questions
FOR EACH ROW
WHEN NEW.user_question_number IS NULL
BEGIN
UPDATE questions
SET user_question_number = (
SELECT COALESCE(MAX(user_question_number), 0) + 1
FROM questions q2
WHERE q2.to_user_id = NEW.to_user_id
AND q2.status != 'deleted'
)
WHERE id = NEW.id;
END;
-- Триггер для пересчета номеров при удалении
CREATE TRIGGER recalculate_user_question_numbers_on_delete
AFTER UPDATE ON questions
FOR EACH ROW
WHEN NEW.status = 'deleted' AND OLD.status != 'deleted'
BEGIN
UPDATE questions
SET user_question_number = user_question_number - 1
WHERE to_user_id = NEW.to_user_id
AND user_question_number > OLD.user_question_number
AND status != 'deleted';
UPDATE questions
SET user_question_number = NULL
WHERE id = NEW.id;
END;
```
#### Обработка команд
```python
@router.message(Command("start"))
@inject_start_services
async def cmd_start(
message: Message,
state: FSMContext,
user_service: UserService,
auth: AuthService,
utils: UtilsService,
message_service: MessageService,
validator: InputValidator
):
# Создаем или обновляем пользователя
user = await user_service.create_or_update_user(message.from_user, message.chat.id)
# Проверяем права
is_admin = auth.is_admin(user.telegram_id)
# Отправляем приветствие
await message_service.send_message(message, welcome_text, keyboard)
```
### Преимущества специализированных декораторов
**Нет проблем с `dispatcher`** - aiogram не передает лишние параметры
**Меньше зависимостей** - инжектируются только нужные сервисы
**Лучшая производительность** - меньше объектов создается
**Более явный код** - видно, какие зависимости используются
**Легче тестировать** - меньше моков нужно создавать
### Тестирование
Система DI значительно упрощает тестирование:
```python
# Создание тестовых зависимостей
@pytest.fixture
async def test_dependencies():
deps = Dependencies()
deps._database = AsyncMock() # Мокаем БД
deps._auth = MagicMock() # Мокаем авторизацию
return deps
# Тестирование обработчиков
@pytest.mark.asyncio
async def test_cmd_start(test_dependencies):
# Настраиваем моки
test_dependencies._user_service.get_user_by_telegram_id.return_value = mock_user
test_dependencies._auth.is_admin.return_value = False
# Тестируем обработчик
result = await cmd_start(
message,
state,
user_service=test_dependencies.user_service,
auth=test_dependencies.auth,
utils=test_dependencies.utils,
message_service=test_dependencies.message_service,
validator=test_dependencies.validator
)
# Проверяем результат
assert result is not None
```
Подробная документация: [DI_SETUP.md](DI_SETUP.md)
## 📊 База данных
Бот использует SQLite для хранения данных:
### Таблицы:
- **users**: Информация о пользователях (ID, имя, ссылка, статус, права суперпользователя)
- **questions**: Вопросы и ответы (текст, статус, анонимность, локальная нумерация)
- **user_blocks**: Блокировки пользователей
- **user_settings**: Настройки пользователей (уведомления, язык)
### Особенности:
- **Внешние ключи**: Связи между таблицами
- **Триггеры**: Автоматическое обновление timestamps и нумерация вопросов
- **Индексы**: Оптимизация запросов, включая индексы для локальной нумерации
- **CRUD операции**: Полный набор операций для каждой таблицы
- **Локальная нумерация**: Каждый пользователь видит свои вопросы с номерами #1, #2, #3...
### Автоматическая нумерация вопросов:
- **Триггер `calculate_user_question_number`**: Автоматически присваивает номер при создании вопроса
- **Триггер `recalculate_user_question_numbers_on_delete`**: Пересчитывает номера при удалении вопроса
- **Удаленные вопросы**: Не участвуют в нумерации (status = 'deleted')
- **Уникальные номера**: Гарантируется уникальность номеров в рамках каждого пользователя
### Схема:
Схема базы данных находится в файле `database/schema.sql`
## 🔧 Настройка
Все настройки находятся в файле `.env`:
- `BOT_TOKEN` - токен бота (обязательно)
- `ADMINS` - список ID администраторов
- `DATABASE_PATH` - путь к файлу базы данных
- `DEBUG` - режим отладки
- `MAX_QUESTION_LENGTH` - максимальная длина вопроса
- `MAX_ANSWER_LENGTH` - максимальная длина ответа
#### Настройки Rate Limiting:
- `RATE_LIMIT_ENV` - окружение (development/production/strict)
- `RATE_LIMIT_MESSAGES_PER_SECOND` - сообщений в секунду на чат
- `RATE_LIMIT_BURST_LIMIT` - максимум сообщений подряд
- `RATE_LIMIT_RETRY_MULTIPLIER` - множитель для задержки при retry
- `RATE_LIMIT_MAX_RETRY_DELAY` - максимальная задержка между попытками
- `RATE_LIMIT_MAX_RETRIES` - максимальное количество повторных попыток
## 🚀 Развертывание
### Локальный запуск:
```bash
python main.py
```
### Docker (планируется):
```bash
docker build -t anonbot .
docker run -d --name anonbot anonbot
```
### VPS/Сервер:
1. Загрузите код на сервер
2. Установите зависимости: `pip install -r requirements.txt`
3. Настройте `.env` файл
4. **Для обновления существующей базы данных** (если нужно добавить поле `is_superuser`):
```bash
python3 -c "
import asyncio
import aiosqlite
async def migrate():
async with aiosqlite.connect('database/anon_qna.db') as conn:
await conn.execute('ALTER TABLE users ADD COLUMN is_superuser BOOLEAN DEFAULT FALSE')
await conn.execute('CREATE INDEX IF NOT EXISTS idx_users_is_superuser ON users(is_superuser)')
await conn.commit()
print('Миграция завершена!')
asyncio.run(migrate())
"
```
5. Запустите: `python main.py`
6. Рекомендуется использовать systemd или supervisor для автозапуска
## 📝 Система логирования
В проекте настроена продвинутая система логирования с использованием библиотеки **loguru** и автоматических декораторов. Логи выводятся в stderr для корректной работы в Docker контейнерах.
### Основные компоненты
- **services/infrastructure/logger.py** - основная настройка системы логирования
- **services/infrastructure/logging_decorators.py** - декораторы для автоматического логирования
- **services/infrastructure/logging_utils.py** - утилиты для контекстного логирования
- **loguru** - библиотека для логирования (уже добавлена в requirements.txt)
### Уровни логирования
- **INFO** - основная информация о работе бота
- **WARNING** - предупреждения о потенциальных проблемах
- **ERROR** - ошибки, требующие внимания
### Формат логов
```
2024-01-15 10:30:45 | INFO | bot:main:25 - 🚀 Запуск бота в режиме polling
2024-01-15 10:30:45 | INFO | loader:init_bot:28 - 🤖 Инициализация Telegram бота
2024-01-15 10:30:45 | INFO | loader:init_bot:33 - ✅ Бот успешно инициализирован
```
### 🎯 Автоматические декораторы логирования
Система включает в себя набор декораторов для автоматического логирования:
#### 1. Основные декораторы
**`@log_function_call`** - логирование входа/выхода из функций
```python
@log_function_call(log_params=True, log_result=True)
async def create_question(self, from_user_id: int, to_user_id: int, message_text: str):
# Автоматически логирует вход с параметрами и выход с результатом
```
**`@log_business_event`** - логирование бизнес-событий
```python
@log_business_event("create_question", log_params=True, log_result=True)
async def create_question(self, ...):
# Логирует бизнес-событие с контекстом
```
**`@log_fsm_transition`** - логирование FSM переходов
```python
@log_fsm_transition(to_state="waiting_for_answer")
async def answer_question_callback(callback: CallbackQuery, state: FSMContext):
# Логирует переходы между состояниями FSM
```
#### 2. Оптимизированные декораторы
**`@log_middleware`** - тихое логирование для middleware
```python
@log_middleware(log_params=True, log_result=False)
async def __call__(self, handler, event, data):
# Логирует только ошибки, вход/выход в DEBUG режиме
```
**`@log_utility`** - декоратор для служебных функций
```python
@log_utility
def _has_attachments(message: Message) -> bool:
# Логирует только ошибки
```
#### 3. Контекстное логирование
**`LoggingContext`** - контекстное логирование с дополнительной информацией
```python
context = get_logging_context(__name__)
context.add_context("user_id", user_id)
context.log_info("Пользователь выполнил действие")
```
**Специальные функции** для бизнес-событий:
```python
log_question_created(logger, question_id, from_user_id, to_user_id)
log_user_created(logger, user_id, username)
log_user_blocked(logger, user_id, reason)
```
### Покрытие логированием
#### 1. Запуск и инициализация
- ✅ Инициализация бота
- ✅ Инициализация диспетчера
- ✅ Инициализация базы данных
- ✅ Регистрация обработчиков
- ✅ Уведомления администраторов
#### 2. База данных (CRUD операции)
- ✅ Создание пользователей
- ✅ Создание вопросов
- ✅ Обновление вопросов
- ✅ Инициализация БД
#### 3. Обработчики команд
- ✅ Команда /start
- ✅ Обработка deep links
- ✅ Создание/обновление пользователей
#### 4. Бизнес-логика
- ✅ Отправка ответов авторам
- ✅ Обработка ошибок
- ✅ Валидация данных
#### 5. Системные события
- ✅ Ошибки в обработчиках
- ✅ Очистка ресурсов
- ✅ Остановка бота
#### 6. FSM состояния
- ✅ Переходы между состояниями
- ✅ Обработка FSM событий
- ✅ Отслеживание пользовательских сессий
### Использование в коде
#### Импорт логгера и декораторов
```python
from services.infrastructure.logger import get_logger
from services.infrastructure.logging_decorators import (
log_function_call, log_business_event, log_fsm_transition,
log_middleware, log_utility
)
from services.infrastructure.logging_utils import (
log_user_action, log_business_operation, log_question_created
)
logger = get_logger(__name__)
```
#### Примеры использования декораторов
```python
# Бизнес-события
@log_business_event("create_question", log_params=True, log_result=True)
async def create_question(self, from_user_id: int, to_user_id: int, message_text: str):
# Автоматически логирует создание вопроса
pass
# FSM переходы
@log_fsm_transition(to_state="waiting_for_answer")
async def answer_question_callback(callback: CallbackQuery, state: FSMContext):
# Логирует переход в состояние ответа
pass
# Middleware (тихое логирование)
@log_middleware(log_params=True, log_result=False)
async def __call__(self, handler, event, data):
# Логирует только ошибки
pass
# Служебные функции
@log_utility
def _has_attachments(message: Message) -> bool:
# Логирует только ошибки
pass
```
#### Контекстное логирование
```python
# Создание контекста
context = get_logging_context(__name__)
context.add_context("user_id", user_id)
context.add_context("question_id", question_id)
context.log_info("Пользователь ответил на вопрос")
# Специальные функции для бизнес-событий
log_question_created(logger, question_id, from_user_id, to_user_id)
log_user_created(logger, user_id, username)
log_user_blocked(logger, user_id, reason)
```
#### Традиционное логирование
```python
# Информационные сообщения
logger.info("🚀 Запуск бота в режиме polling")
logger.info(f"👤 Создание пользователя: {user.telegram_id} ({user.first_name})")
# Предупреждения
logger.warning("⚠️ Список администраторов пуст")
logger.warning(f"⚠️ Не удалось отправить уведомление админу {admin_id}: {e}")
# Ошибки
logger.error(f"💥 Ошибка при запуске бота: {e}")
logger.error(f"💥 Ошибка в обработчике /start: {e}")
```
### Docker интеграция
Логи настроены для вывода в stderr, что обеспечивает корректную работу в Docker:
```dockerfile
# В Dockerfile
CMD ["python", "main.py"]
```
```bash
# Просмотр логов в Docker
docker logs <container_name>
```
### Файловое логирование (DEBUG режим)
В режиме отладки логи также сохраняются в файлы:
- **logs/bot.log** - основные логи
- Ротация: 10 MB
- Хранение: 7 дней
- Сжатие: zip
### ⚡ Производительность и оптимизация
#### Тихие декораторы
Для предотвращения избыточного логирования используются тихие декораторы:
- **`@log_middleware`** - для middleware (логирует только ошибки)
- **`@log_utility`** - для служебных функций (логирует только ошибки)
- **`quiet=True`** - параметр для полного отключения логирования входа/выхода
#### Уровни логирования
- **INFO** - бизнес-события и важные операции
- **DEBUG** - детальная информация (только в DEBUG режиме)
- **WARNING** - предупреждения
- **ERROR** - ошибки (всегда логируются)
#### Контекстная информация
Декораторы автоматически извлекают контекстную информацию:
- `user_id` - ID пользователя
- `question_id` - ID вопроса
- `page` - номер страницы
- `status` - статус операции
#### Примеры оптимизированного логирования
```python
# Middleware - тихое логирование
@log_middleware(log_params=True, log_result=False)
async def __call__(self, handler, event, data):
# Логирует только ошибки, вход/выход в DEBUG режиме
pass
# Служебные функции - только ошибки
@log_utility
def _has_attachments(message: Message) -> bool:
# Логирует только ошибки
pass
# Бизнес-функции - полное логирование
@log_business_event("create_question", log_params=True, log_result=True)
async def create_question(self, ...):
# Полное логирование для важных операций
pass
```
### Мониторинг
#### Ключевые метрики для мониторинга
1. **Запуск/остановка бота**
- `🚀 Запуск бота в режиме polling`
- `🛑 Бот остановлен`
2. **Пользователи**
- `👤 Создание пользователя`
- `👤 Обновление существующего пользователя`
3. **Вопросы и ответы**
- `❓ Создание вопроса`
- `📝 Обновление вопроса`
- `📤 Отправка ответа`
4. **Ошибки**
- `💥 Ошибка в обработчике`
- `⚠️ Предупреждения`
#### Алерты
Рекомендуется настроить алерты на:
- ERROR уровень логов
- Отсутствие логов более 5 минут
- Частые ошибки в обработчиках
### Настройка уровней
Уровень логирования настраивается через переменную окружения:
```bash
# В .env файле
DEBUG=true # DEBUG уровень
DEBUG=false # INFO уровень (по умолчанию)
```
### Производительность
- Логирование асинхронное
- Минимальное влияние на производительность
- Эмодзи в логах для быстрого визуального поиска
- Структурированный формат для парсинга
## 🤝 Вклад в проект
1. Fork репозитория
2. Создайте feature branch
3. Внесите изменения
4. Создайте Pull Request
## 📄 Лицензия
MIT License
## 🆘 Поддержка
Если у вас возникли проблемы:
1. Проверьте логи бота
2. Убедитесь в правильности настройки `.env`
3. Проверьте права доступа к файлам
4. Обратитесь к администратору
### Дополнительная документация:
- [Руководство по суперпользователям](SUPERUSER_DISPLAY_FEATURE.md) - подробное описание системы ролей и функционала для суперпользователей
- [Настройка Dependency Injection](DI_SETUP.md) - подробное руководство по системе инъекции зависимостей
- [Примеры использования DI](examples/dependency_injection_example.py) - практические примеры использования системы инъекции зависимостей
## 📈 Prometheus метрики
AnonBot поддерживает экспорт метрик в формате Prometheus для мониторинга и анализа производительности.
### Эндпоинты
- **http://localhost:8081/metrics** - экспорт метрик Prometheus
- **http://localhost:8081/health** - проверка здоровья бота
- **http://localhost:8081/ready** - готовность к работе (readiness probe)
- **http://localhost:8081/** - информация о сервисе
### Доступные метрики
#### Информационные метрики
- `anon_bot_info` - Информация о боте (версия, сервис)
#### Счетчики сообщений
- `anon_bot_messages_total` - Общее количество обработанных сообщений
- Метки: `message_type`, `status`
#### Счетчики вопросов
- `anon_bot_questions_total` - Общее количество вопросов
- Метки: `status` (created, rejected, deleted)
#### Счетчики ответов
- `anon_bot_answers_total` - Общее количество ответов
- Метки: `status` (sent, edited, delivered, delivery_failed)
#### Счетчики пользователей
- `anon_bot_users_total` - Общее количество пользователей
- Метки: `action` (created, updated)
#### Счетчики ошибок
- `anon_bot_errors_total` - Общее количество ошибок
- Метки: `error_type`, `component`
#### HTTP метрики
- `anon_bot_http_requests_total` - Общее количество HTTP запросов
- Метки: `method`, `endpoint`, `status_code`
- `anon_bot_http_request_duration_seconds` - Время обработки HTTP запросов
- Метки: `method`, `endpoint`
#### Время обработки
- `anon_bot_message_processing_seconds` - Время обработки сообщений
- Метки: `message_type`
- `anon_bot_question_processing_seconds` - Время обработки вопросов
- `anon_bot_answer_processing_seconds` - Время обработки ответов
#### Gauge метрики
- `anon_bot_active_users` - Количество активных пользователей
- `anon_bot_active_questions` - Количество активных вопросов
### Настройка Prometheus
Добавьте в конфигурацию Prometheus (`prometheus.yml`):
```yaml
scrape_configs:
- job_name: 'anon-bot'
static_configs:
- targets: ['localhost:8081']
metrics_path: '/metrics'
scrape_interval: 30s
```
### Примеры запросов PromQL
#### Количество сообщений в секунду
```promql
rate(anon_bot_messages_total[5m])
```
#### Количество ошибок в секунду
```promql
rate(anon_bot_errors_total[5m])
```
#### Время обработки сообщений (95-й процентиль)
```promql
histogram_quantile(0.95, rate(anon_bot_message_processing_seconds_bucket[5m]))
```
#### Количество активных пользователей
```promql
anon_bot_active_users
```
### Мониторинг в Docker
При запуске в Docker убедитесь, что порт 8081 доступен:
```yaml
ports:
- "8081:8081"
```
### Алерты
Рекомендуется настроить алерты на:
1. **Высокий уровень ошибок**
```promql
rate(anon_bot_errors_total[5m]) > 0.1
```
2. **Медленную обработку**
```promql
histogram_quantile(0.95, rate(anon_bot_message_processing_seconds_bucket[5m])) > 5
```
3. **Недоступность сервиса**
```promql
up{job="anon-bot"} == 0
```
### Безопасность
Эндпоинты метрик не требуют аутентификации. В продакшене рекомендуется:
- Ограничить доступ к эндпоинтам через firewall
- Использовать reverse proxy с аутентификацией
- Настроить TLS для HTTPS
## 🔧 Исправление проблем с метриками
### 🔍 Найденные проблемы
В дашбордах Grafana не отображались следующие метрики:
- Database Connections - Active
- Database Performance - Query Duration
- Active Questions
- Active Users
- Answers per Minute
- Live Activity - Active Users
### 🛠️ Внесенные исправления
#### 1. Создан сервис MetricsUpdater (`services/infrastructure/metrics_updater.py`)
**Исправление циклической зависимости**: Первоначально возникла циклическая зависимость между `metrics_updater.py` и `dependencies.py`. Проблема была решена путем:
- Удаления импорта `get_database_service` из `dependencies`
- Передачи пути к БД напрямую в конструктор `MetricsUpdater`
- Создания собственного экземпляра `DatabaseService` внутри `MetricsUpdater`
**Исправление использования логгера**: Первоначально использовался `loguru` напрямую, но в проекте уже есть настроенная система логирования. Исправлено:
- Заменен `from loguru import logger` на `from .logger import get_logger`
- Используется `self.logger = get_logger(__name__)` в конструкторе
- Все вызовы `logger` заменены на `self.logger`
**Проблема**: Методы `set_active_users()` и `set_active_questions()` были определены в `MetricsService`, но нигде не вызывались.
**Решение**: Создан сервис `MetricsUpdater`, который:
- Периодически обновляет количество активных пользователей (за последние 24 часа)
- Периодически обновляет количество активных вопросов (статус pending/processing)
- Обновляет метрики соединений с БД
- Запускается автоматически при старте бота
#### 2. Создан декоратор для метрик БД (`services/infrastructure/db_metrics_decorator.py`)
**Проблема**: Методы `record_db_connection()` и `record_db_query()` были определены, но не интегрированы в код БД.
**Решение**: Создан декоратор `track_db_operation`, который:
- Автоматически записывает время выполнения операций БД
- Отслеживает успешные и неудачные операции
- Записывает метрики соединений с БД
- Использует существующую систему логирования проекта
**Интеграция декораторов**: Для избежания циклических зависимостей создан патч `crud_metrics_patch.py`:
- Применяет декораторы к CRUD операциям после их импорта
- Автоматически активируется при импорте модуля
- Покрывает основные операции: INSERT, SELECT, UPDATE для users и questions
- **Исправлено**: Убрано применение `track_db_connection` к `@asynccontextmanager` методам (ошибка `__aenter__`)
#### 3. Обновлен bot.py
**Изменения**:
- Добавлен запуск `MetricsUpdater` при старте бота
- Добавлена остановка `MetricsUpdater` при завершении работы
- Передача пути к БД в `MetricsUpdater`: `config.DATABASE_PATH`
- Интервал обновления метрик: 30 секунд
#### 4. Обновлен __init__.py
**Изменения**:
- Добавлен экспорт новых сервисов и декораторов
- Обновлен список `__all__`
### 📊 Ожидаемые результаты
После внесения исправлений в дашбордах Grafana должны отображаться:
#### AnonBot Overview:
- ✅ **Active Users** - количество активных пользователей за 24 часа
- ✅ **Active Questions** - количество активных вопросов (pending/processing)
- ✅ **Live Activity - Active Users** - то же значение, что и Active Users
- ✅ **Answers per Minute** - скорость отправки ответов
#### Performance AnonBot:
- ✅ **Database Connections - Active** - количество активных соединений с БД
- ✅ **Database Performance - Query Duration** - время выполнения запросов к БД
#### Server Monitoring:
- ✅ **AnonBot System Health** - активные пользователи
- ✅ **AnonBot Active Questions** - активные вопросы
- ✅ **AnonBot Database Connections** - соединения с БД
### 🚀 Развертывание исправлений
1. **Перезапустите AnonBot**:
```bash
docker-compose restart anon-bot
```
2. **Проверьте логи**:
```bash
docker-compose logs -f anon-bot
```
Должны появиться сообщения:
```
📊 Запуск обновления метрик...
📊 MetricsUpdater запущен с интервалом 30 секунд
```
3. **Проверьте метрики**:
```bash
curl http://localhost:8081/metrics | grep anon_bot_active
```
4. **Проверьте в Grafana**:
- Откройте дашборды AnonBot
- Дождитесь обновления данных (до 30 секунд)
- Проверьте отображение метрик
### 🔧 Дополнительные настройки
#### Изменение интервала обновления метрик
В файле `bot.py` можно изменить интервал обновления:
```python
# Текущий интервал: 30 секунд
await start_metrics_updater(update_interval=30)
# Для более частого обновления (например, 10 секунд):
await start_metrics_updater(update_interval=10)
```
#### Добавление метрик БД в CRUD операции
Для автоматического сбора метрик БД в CRUD операциях добавьте декоратор:
```python
from services.infrastructure import track_db_operation
@track_db_operation("SELECT", "users")
async def get_user(self, user_id: int):
# код метода
pass
```
### 📈 Мониторинг
После внесения исправлений рекомендуется настроить алерты:
1. **Низкая активность пользователей**: `anon_bot_active_users < 1`
2. **Много активных вопросов**: `anon_bot_active_questions > 100`
3. **Проблемы с БД**: `anon_bot_db_connections_active == 0`
4. **Высокое время ответа БД**: `histogram_quantile(0.95, rate(anon_bot_db_query_duration_seconds_bucket[5m])) > 1`
### 🐛 Troubleshooting
#### Ошибка циклической зависимости
**Проблема**: `ImportError: cannot import name 'get_database_service' from partially initialized module 'dependencies'`
**Решение**: Проблема была исправлена в версии 2.0 исправлений:
- Удален импорт `get_database_service` из `metrics_updater.py`
- Добавлен параметр `db_path` в конструктор `MetricsUpdater`
- `DatabaseService` создается внутри `MetricsUpdater` с переданным путем к БД
#### Метрики не обновляются
1. Проверьте логи AnonBot:
```bash
docker-compose logs anon-bot | grep -i metrics
```
2. Проверьте доступность эндпоинта:
```bash
curl http://localhost:8081/metrics
```
3. Проверьте конфигурацию Prometheus:
```bash
curl http://localhost:9090/api/v1/targets | grep anon-bot
```
#### Ошибки в логах
Если появляются ошибки типа "Database connection failed", проверьте:
- Доступность базы данных
- Правильность пути к БД в конфигурации
- Права доступа к файлу БД
#### Нулевые значения в дашбордах
Если метрики отображаются, но имеют нулевые значения:
- Убедитесь, что в БД есть данные (пользователи, вопросы)
- Проверьте SQL запросы в `MetricsUpdater`
- Увеличьте интервал обновления для накопления данных
## 🐳 Docker
### Сборка образа
```bash
docker build -t anon-bot .
```
### Запуск контейнера
```bash
docker run -d \
--name anon-bot \
--restart unless-stopped \
-p 8081:8081 \
-v $(pwd)/database:/app/database \
-v $(pwd)/logs:/app/logs \
-e BOT_TOKEN=your_bot_token_here \
-e ADMINS=123456789,987654321 \
-e DEBUG=false \
anon-bot
```
### Управление контейнером
```bash
# Просмотр логов
docker logs anon-bot
# Остановка контейнера
docker stop anon-bot
# Запуск контейнера
docker start anon-bot
# Удаление контейнера
docker rm anon-bot
# Перезапуск контейнера
docker restart anon-bot
```
### Переменные окружения
Все переменные окружения из `.env_example` можно передать через `-e`:
```bash
docker run -d \
--name anon-bot \
-p 8081:8081 \
-e BOT_TOKEN=your_token \
-e ADMINS=123456789 \
-e DEBUG=false \
-e MAX_QUESTION_LENGTH=1000 \
-e MAX_ANSWER_LENGTH=2000 \
anon-bot
```
### Volumes
- `./database:/app/database` - персистентное хранение базы данных
- `./logs:/app/logs` - персистентное хранение логов
### Порты
- `8081:8081` - порт для метрик и health check
### Health Check
Контейнер включает встроенный health check:
```bash
# Проверка статуса
docker inspect --format='{{.State.Health.Status}}' anon-bot
# Просмотр деталей health check
docker inspect anon-bot | jq '.[0].State.Health'
```
**Параметры health check:**
- **Интервал проверки**: 30 секунд
- **Таймаут**: 10 секунд
- **Период запуска**: 60 секунд (время на инициализацию)
- **Количество попыток**: 3
- **Команда проверки**: `curl -f http://localhost:8081/health`
**Статусы health check:**
- `starting` - контейнер запускается
- `healthy` - контейнер здоров
- `unhealthy` - контейнер нездоров
## 🚦 Rate Limiting
AnonBot включает комплексную систему rate limiting для предотвращения ошибок Flood Control в Telegram Bot API. Система автоматически ограничивает скорость отправки сообщений и обрабатывает ошибки с повторными попытками.
### Рекомендуемые настройки на основе лимитов Telegram API
Система настроена с учетом официальных лимитов Telegram Bot API:
- **1 сообщение в секунду** в личных чатах
- **20 сообщений в минуту** в групповых чатах (0.33 в секунду)
- **30 запросов в секунду** глобально
Настройки по умолчанию используют консервативные значения (50% от лимитов) для обеспечения стабильной работы.
### Компоненты системы
#### 1. Конфигурация (`services/rate_limit_config.py`)
- **RateLimitSettings**: Основные настройки rate limiting
- **Конфигурации для разных окружений**: development, production, strict
- **Адаптивная конфигурация**: Автоматическая настройка на основе уровня ошибок
#### 2. Rate Limiter (`services/rate_limiter.py`)
- **ChatRateLimiter**: Ограничения для конкретного чата
- **GlobalRateLimiter**: Глобальные ограничения для всех чатов
- **RetryHandler**: Обработка повторных попыток с экспоненциальной задержкой
- **TelegramRateLimiter**: Основной класс, объединяющий все компоненты
#### 3. Сервис (`services/rate_limit_service.py`)
- **RateLimitService**: Высокоуровневый сервис для управления rate limiting
- **Статистика**: Отслеживание успешных/неудачных запросов
- **Адаптация**: Автоматическая настройка конфигурации
#### 4. Middleware (`middlewares/rate_limit_middleware.py`)
- **RateLimitMiddleware**: Автоматическое применение rate limiting ко всем сообщениям
- **Прозрачная интеграция**: Минимальные изменения в существующем коде
#### 5. Validation Middleware (`middlewares/validation_middleware.py`)
- **ValidationMiddleware**: Автоматическая валидация всех входящих данных
- **Безопасность**: Защита от некорректных данных и атак
- **Логирование**: Оптимизированное логирование с тихими декораторами для middleware
### Настройка
#### Переменные окружения
Добавьте следующие переменные в ваш `.env` файл:
```env
# Окружение для rate limiting (development/production/strict)
RATE_LIMIT_ENV=production
# Основные настройки rate limiting
RATE_LIMIT_MESSAGES_PER_SECOND=0.5
RATE_LIMIT_BURST_LIMIT=2
RATE_LIMIT_RETRY_MULTIPLIER=1.5
RATE_LIMIT_MAX_RETRY_DELAY=30.0
RATE_LIMIT_MAX_RETRIES=3
# Задержки для разных типов сообщений
RATE_LIMIT_VOICE_DELAY=2.0
RATE_LIMIT_MEDIA_DELAY=1.5
RATE_LIMIT_TEXT_DELAY=1.0
# Множители для разных типов чатов
RATE_LIMIT_PRIVATE_MULTIPLIER=1.0
RATE_LIMIT_GROUP_MULTIPLIER=0.8
RATE_LIMIT_CHANNEL_MULTIPLIER=0.6
# Глобальные ограничения
RATE_LIMIT_GLOBAL_MESSAGES_PER_SECOND=10.0
RATE_LIMIT_GLOBAL_BURST_LIMIT=20
```
#### Конфигурации по умолчанию
**Production (по умолчанию)**
- 0.5 сообщений в секунду на чат (50% от лимита Telegram API)
- Максимум 2 сообщения подряд
- 3 повторные попытки при ошибках
- Максимальная задержка 30 секунд
- 20 глобальных запросов в секунду (из 30 доступных)
**Development**
- 0.8 сообщений в секунду на чат (80% от лимита для тестирования)
- Максимум 3 сообщения подряд
- 2 повторные попытки при ошибках
- Максимальная задержка 15 секунд
**Strict**
- 0.3 сообщений в секунду на чат (30% от лимита для максимальной стабильности)
- Максимум 1 сообщение подряд
- 5 повторных попыток при ошибках
- Максимальная задержка 60 секунд
- 10 глобальных запросов в секунду (консервативные настройки)
### Использование
#### Автоматическое использование (рекомендуется)
Rate limiting и валидация автоматически применяются ко всем сообщениям через middleware. Никаких изменений в коде не требуется.
**ValidationMiddleware** автоматически валидирует:
- Все callback queries
- Все сообщения
- Telegram ID пользователей
- Username (если есть)
- Chat ID
#### Ручное использование
```python
from services.rate_limiting.rate_limit_service import RateLimitService
from dependencies import get_rate_limit_service
# Получение сервиса через DI
rate_limit_service = get_rate_limit_service()
# Отправка сообщения с rate limiting
result = await rate_limit_service.send_with_rate_limit(
bot.send_message,
chat_id=user_id,
text="Привет!"
)
```
#### Прямое использование rate limiter
```python
from services.rate_limiting.rate_limiter import send_with_rate_limit
# Отправка с автоматическим rate limiting
result = await send_with_rate_limit(
bot.send_message,
chat_id=user_id,
text="Привет!"
)
```
#### Ручное использование валидатора
```python
from services.validation import InputValidator
from dependencies import get_validator
# Получение валидатора через DI
validator = get_validator()
# Валидация текста вопроса
result = validator.validate_question_text(text, max_length=1000)
if not result:
print(f"Ошибка: {result.error_message}")
else:
sanitized_text = result.sanitized_value
# Валидация callback data
result = validator.validate_callback_data(callback_data)
if not result:
await callback.answer("❌ Неверные данные", show_alert=True)
return
```
### Административные функции
Доступ к управлению rate limiting осуществляется через админскую панель:
1. **Перейти в админку**: `/admin`
2. **Нажать кнопку "🚦 Rate Limiting"**
3. **Выбрать нужное действие**:
- **📊 Статистика Rate Limiting** - показывает подробную статистику:
- Общее количество запросов
- Процент успеха/ошибок
- Количество RetryAfter ошибок
- Среднее время ожидания
- **🔄 Сбросить статистику** - сбрасывает всю статистику rate limiting
- **⚙️ Адаптировать конфигурацию** - адаптирует настройки на основе текущей производительности
**Важно**: Доступ к функциям rate limiting имеют только администраторы бота (не суперпользователи).
### Мониторинг
#### Логирование
Система логирует:
- Rate limiting события (DEBUG уровень)
- RetryAfter ошибки (WARNING уровень)
- Критические ошибки (ERROR уровень)
#### Статистика
RateLimitService отслеживает:
- Общее количество запросов
- Успешные/неудачные запросы
- Типы ошибок (RetryAfter, API ошибки)
- Время ожидания
#### Адаптивная конфигурация
Система автоматически адаптирует настройки:
- При >10% ошибок: ужесточает ограничения
- При <1% ошибок: ослабляет ограничения
- Требует минимум 100 запросов для адаптации
### Интеграция с DI
Rate limiting полностью интегрирован в систему инъекции зависимостей:
```python
# В обработчиках
async def some_handler(
message: Message,
rate_limit_service: RateLimitService
):
# rate_limit_service автоматически инжектируется
pass
```
### Архитектурные особенности
#### Соблюдение принципов
1. **Single Responsibility**: Каждый компонент отвечает за свою задачу
2. **Open/Closed**: Легко расширяется новыми типами ограничений
3. **Dependency Inversion**: Зависит от абстракций, а не от конкретных реализаций
#### Производительность
- Минимальные накладные расходы
- Эффективное управление памятью
- Асинхронная обработка
#### Надежность
- Обработка всех типов ошибок Telegram API
- Экспоненциальная задержка при повторных попытках
- Автоматическое восстановление после ошибок
### Устранение неполадок
#### Высокий процент ошибок
1. Проверьте статистику: `/ratelimit_stats`
2. Адаптируйте конфигурацию: `/adapt_ratelimit`
3. При необходимости ужесточите настройки в `.env`
#### Медленная отправка сообщений
1. Проверьте настройки `RATE_LIMIT_MESSAGES_PER_SECOND`
2. Увеличьте значение для более быстрой отправки
3. Убедитесь, что не превышаете лимиты Telegram API
#### Проблемы с интеграцией
1. Убедитесь, что middleware зарегистрирован в `loader.py`
2. **Ошибка `TypeError: got an unexpected keyword argument 'dispatcher'`**:
- **Причина**: aiogram 3.x передает `dispatcher` во все обработчики сообщений, но `@inject_all` не обрабатывает его правильно
- **Решение**: Используйте специализированные декораторы вместо `@inject_all`:
```python
# ❌ Неправильно
@inject_all
async def process_question(message: Message, dispatcher, ...):
# ✅ Правильно
@inject_question_services
async def process_question(message: Message, question_service: QuestionService, ...):
```
- **Преимущества**: Нет проблем с `dispatcher`, меньше зависимостей, лучшая производительность
3. Проверьте, что все зависимости корректно импортированы
4. Проверьте логи на наличие ошибок инициализации
## 🔮 Планы развития
- [x] **Система суперпользователей** - расширенные права для модерации с отображением информации об авторах
- [x] **Система инъекции зависимостей** - современная архитектура с DI для лучшей тестируемости
- [x] **Система разрешений** - гибкая система разрешений с соблюдением принципа OCP
- [x] **Prometheus метрики** - мониторинг производительности и ошибок
- [x] **Rate limiting** - защита от спама и DDoS с автоматической адаптацией
- [x] **Реорганизация структуры проекта** - улучшенная архитектура с логической группировкой сервисов
- [x] **Локальная нумерация вопросов** - каждый пользователь видит свои вопросы с номерами #1, #2, #3... вместо глобальных ID
- [ ] **Unit-тесты** - полное покрытие тестами с использованием DI
- [ ] **Кэширование Redis** - оптимизация производительности
- [ ] Рассылка сообщений
- [ ] Экспорт данных
- [ ] Веб-интерфейс для админов
- [ ] Поддержка медиафайлов в вопросах
- [ ] Категории вопросов
- [ ] Модерация контента
- [ ] Аналитика и отчеты
## 📝 Changelog
### v1.7.0 - Продвинутая система логирования (2025-01-27)
#### ✨ Новые возможности
- **Автоматические декораторы логирования** - `@log_function_call`, `@log_business_event`, `@log_fsm_transition`
- **Контекстное логирование** - `LoggingContext` с дополнительной информацией
- **Оптимизированные декораторы** - `@log_middleware`, `@log_utility` для предотвращения избыточного логирования
- **FSM отслеживание** - автоматическое логирование переходов между состояниями
- **Специальные функции** - `log_question_created`, `log_user_created`, `log_user_blocked`
- **Тихие декораторы** - предотвращение рекурсивного логирования в middleware
#### 🔧 Улучшения
- **100% покрытие логированием** - все функции теперь логируются автоматически
- **Улучшенная производительность** - оптимизированное логирование для middleware
- **Детальная диагностика** - полная трассировка выполнения функций
- **Контекстная информация** - автоматическое извлечение user_id, question_id и других параметров
#### 📁 Новые файлы
- `services/infrastructure/logging_decorators.py` - декораторы для автоматического логирования
- `services/infrastructure/logging_utils.py` - утилиты для контекстного логирования
### v1.6.0 - Локальная нумерация вопросов (2025-01-27)
#### ✨ Новые возможности
- **Локальная нумерация вопросов**: Каждый пользователь теперь видит свои вопросы с номерами #1, #2, #3... вместо глобальных ID
- **Автоматическая нумерация**: Триггеры БД автоматически присваивают и пересчитывают номера вопросов
- **Умная нумерация**: Удаленные вопросы не участвуют в нумерации, номера автоматически пересчитываются при удалении
#### 🔧 Технические изменения
- **Новое поле БД**: `user_question_number` в таблице `questions`
- **Триггеры БД**:
- `calculate_user_question_number` - автоматическое вычисление номера при создании
- `recalculate_user_question_numbers_on_delete` - пересчет номеров при удалении
- **Индексы**: Оптимизированные индексы для быстрого поиска по локальным номерам
- **Модель Question**: Новый метод `get_display_number()` для получения номера для отображения
#### 🐛 Исправления
- **Отображение номеров**: Исправлена проблема с несоответствием номеров в тексте сообщений и на кнопках
- **Пагинация**: Обновлена для работы с локальными номерами
- **CRUD операции**: Обновлены для поддержки автоматической нумерации
#### 📊 Улучшения UX
- **Интуитивная нумерация**: Пользователи видят последовательные номера своих вопросов
- **Консистентность**: Номера в тексте и на кнопках всегда совпадают
- **Автоматизация**: Никаких ручных действий для поддержания нумерации