🤖 AnonBot - Telegram бот для анонимных вопросов
Telegram-бот для приема и обработки анонимных вопросов с использованием aiogram 3.x.
✨ Возможности
- 🔗 Персональные ссылки - каждый пользователь получает уникальную ссылку для приема вопросов
- 👤 Анонимность - вопросы отправляются анонимно, личность отправителя скрыта
- 💬 Интерактивные ответы - удобный интерфейс для ответов на вопросы через inline кнопки
- 🔢 Локальная нумерация - каждый пользователь видит свои вопросы с номерами #1, #2, #3... вместо глобальных ID
- 📊 Статистика - подробная статистика для администраторов
- 🗄️ База данных - SQLite для хранения пользователей и вопросов с автоматической нумерацией
- 👑 Админ панель - управление ботом для администраторов
- 🔍 Суперпользователи - расширенные права для модерации с отображением информации об авторах вопросов
- 🏗️ Современная архитектура - система инъекции зависимостей для лучшей тестируемости
- 📝 Продвинутое логирование - автоматические декораторы, контекстное логирование, FSM отслеживание
- 🛡️ Безопасность - валидация данных, обработка ошибок, система ролей
- 🔍 Централизованная валидация - автоматическая валидация всех входных данных с санитизацией
- 🔐 Система разрешений - гибкая система разрешений с соблюдением принципа OCP
- 📈 Prometheus метрики - полный мониторинг производительности и состояния бота
🚀 Быстрый старт
1. Установка зависимостей
pip install -r requirements.txt
pip install dependency-injector # Для системы инъекции зависимостей
2. Настройка конфигурации
Создайте файл .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. Запуск бота
Локальный запуск
python main.py
или
python bot.py
Запуск в Docker
- Соберите Docker образ:
docker build -t anon-bot .
- Запустите контейнер:
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
- Проверьте статус:
docker logs anon-bot
- Проверьте метрики:
curl http://localhost:8081/health
📄 PID файл и мониторинг процесса
AnonBot автоматически создает PID файл для отслеживания процесса и предоставляет детальную информацию о состоянии через HTTP эндпоинты.
PID файл
- Расположение:
/tmp/anon_bot.pid - Содержимое: PID процесса бота
- Автоматическое управление: создается при запуске, удаляется при остановке
- Проверка дублирования: предотвращает запуск нескольких экземпляров
Эндпоинт /status
Предоставляет детальную информацию о процессе:
curl http://localhost:8081/status
Пример ответа:
{
"status": "running",
"pid": 12345,
"uptime": "2ч 15м",
"memory_usage_mb": 45.2,
"cpu_percent": 0.1,
"timestamp": 1705312200.5
}
Поля ответа:
status- статус процесса (running/stopped/not_found/error)pid- идентификатор процессаuptime- время работы в человекочитаемом форматеmemory_usage_mb- использование памяти в МБcpu_percent- загрузка CPU в процентахtimestamp- время ответа
Тестирование
Для тестирования PID функционала можно использовать curl:
# Проверка статуса процесса
curl http://localhost:8081/status
# Проверка всех эндпоинтов
curl http://localhost:8081/
📁 Структура проекта
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 сервер для метрик
│ │ └── pid_manager.py # Менеджер PID файлов
│ ├── 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- константы приложения
✅ Преимущества новой структуры
- Логическая группировка - связанные сервисы в одних папках
- Разделение ответственности - бизнес-логика отдельно от инфраструктуры
- Масштабируемость - легко добавлять новые сервисы в нужные категории
- Читаемость - понятно, где что находится
- Обратная совместимость - старые импорты продолжают работать
🔄 Обратная совместимость
Все импорты обновлены, но старые импорты продолжают работать благодаря __init__.py файлам:
# Старый способ (все еще работает)
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 - проверка корректности всех идентификаторов
- Логирование - полное логирование всех ошибок валидации с автоматическими декораторами
🏗️ Архитектура валидации
# Централизованный валидатор
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:
# ValidationMiddleware автоматически валидирует:
# - Все callback queries
# - Все сообщения
# - Telegram ID пользователей
# - Username (если есть)
# - Chat ID
✅ Преимущества
- Безопасность - защита от некорректных данных и атак
- Стабильность - предотвращение ошибок от невалидных данных
- UX - понятные сообщения об ошибках для пользователей
- Мониторинг - полное логирование проблем валидации
- Централизация - единая точка валидации для всего приложения
🎯 Как это работает
Для пользователей:
- Регистрация: Пользователь запускает бота командой
/start - Получение ссылки: Бот генерирует персональную ссылку формата
t.me/bot_username?start=ref_{anonymous_id} - Публикация ссылки: Пользователь делится ссылкой в социальных сетях
- Получение вопросов: Друзья переходят по ссылке и задают анонимные вопросы
- Ответы: Пользователь получает уведомления и может отвечать на вопросы
Для отправителей вопросов:
- Переход по ссылке: Отправитель переходит по персональной ссылке пользователя
- Задание вопроса: Отправляет вопрос боту
- Анонимность: Вопрос передается получателю анонимно
- Получение ответа: Если получатель ответит, ответ будет показан
🔧 Команды бота
Основные команды:
/start- Запуск бота и получение персональной ссылки/help- Справка по использованию бота
Админ команды:
/stats- Показать статистику бота (только для админов)
👑 Админ панель
Администраторы имеют доступ к дополнительным функциям:
- 📊 Статистика - общая статистика бота, пользователей и вопросов
- 👥 Пользователи - список всех пользователей бота
- ❓ Все вопросы - статистика по всем вопросам
- 📢 Рассылка - функция рассылки (планируется)
- ⚙️ Настройки - просмотр текущих настроек бота
🔍 Система ролей и суперпользователи
Бот поддерживает трехуровневую систему ролей:
👑 Администраторы (admin)
- Определяются в конфигурации (
ADMINSв.env) - Имеют все права доступа
- Не могут быть изменены через базу данных
🔍 Суперпользователи (superuser)
- Определяются в базе данных (поле
is_superuser = TRUE) - Имеют расширенные права для модерации
- Могут видеть информацию об авторах вопросов
Особенности для суперпользователей:
Отображение списка вопросов:
10. ✅ #2 Вопрос от @username FirstName LastName
Уведомления о новых вопросах:
❓ Новый вопрос от @username FirstName LastName!
📝 Вопрос:
Текст вопроса...
📅 05.09.2025 23:27
👤 Обычные пользователи (user)
- Стандартные права доступа
- Видят анонимные вопросы без информации об авторах
Назначение суперпользователя
Суперпользователей можно назначать через базу данных:
UPDATE users SET is_superuser = TRUE WHERE telegram_id = 123456789;
Или программно:
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 |
Ответы на вопросы | Все активные незабаненные пользователи |
🚀 Использование
С декораторами (рекомендуется)
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("Команда выполнена!")
Готовые декораторы
@require_permission("view_stats") # Конкретное разрешение
@require_admin() # Только администраторы
@require_superuser() # Только суперпользователи
@require_admin_or_superuser() # Администраторы или суперпользователи
@require_active_user() # Активные пользователи
@require_unbanned_user() # Незабаненные пользователи
Добавление нового разрешения
# 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. Это обеспечивает:
- Тестируемость: Легко мокать зависимости в тестах
- Читаемость: Явные зависимости в сигнатурах функций
- Гибкость: Легко заменять реализации сервисов
- Обратная совместимость: Старый код продолжает работать
Доступные сервисы
- DatabaseService - работа с базой данных, CRUD операции
- AuthService - авторизация, проверка прав, управление ролями
- InputValidator - централизованная валидация всех входных данных
- UtilsService - форматирование данных, утилиты
- RateLimitService - управление rate limiting, статистика
- Config - доступ к конфигурации приложения
Быстрый старт с DI
# Подключение в 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)
Способы инъекции
-
Специализированные декораторы (рекомендуется):
@inject_question_services- для обработки вопросов@inject_answer_services- для обработки ответов@inject_start_services- для команды /start@inject_link_services- для кнопки "Моя ссылка"@inject_main_menu_services- для кнопки "Главное меню"
-
Базовые декораторы:
@inject_database- только DatabaseService@inject_auth- только AuthService@inject_utils- только UtilsService
-
Автоматически: aiogram инжектит зависимости из middleware
-
Обратная совместимость: старые функции продолжают работать
Примеры использования специализированных декораторов
Обработка вопросов
@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
)
Обработка ответов
@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
# Сохраняем ответ
# ... логика сохранения
Локальная нумерация вопросов
Отображение вопросов с локальными номерами
# В модели 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>"
Автоматическая нумерация через триггеры БД
-- Триггер для автоматического вычисления номера при создании
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;
Обработка команд
@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 значительно упрощает тестирование:
# Создание тестовых зависимостей
@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
📊 База данных
Бот использует 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- множитель для задержки при retryRATE_LIMIT_MAX_RETRY_DELAY- максимальная задержка между попыткамиRATE_LIMIT_MAX_RETRIES- максимальное количество повторных попыток
🚀 Развертывание
Локальный запуск:
python main.py
Docker (планируется):
docker build -t anonbot .
docker run -d --name anonbot anonbot
VPS/Сервер:
- Загрузите код на сервер
- Установите зависимости:
pip install -r requirements.txt - Настройте
.envфайл - Для обновления существующей базы данных (если нужно добавить поле
is_superuser):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()) " - Запустите:
python main.py - Рекомендуется использовать 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 - логирование входа/выхода из функций
@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 - логирование бизнес-событий
@log_business_event("create_question", log_params=True, log_result=True)
async def create_question(self, ...):
# Логирует бизнес-событие с контекстом
@log_fsm_transition - логирование FSM переходов
@log_fsm_transition(to_state="waiting_for_answer")
async def answer_question_callback(callback: CallbackQuery, state: FSMContext):
# Логирует переходы между состояниями FSM
2. Оптимизированные декораторы
@log_middleware - тихое логирование для middleware
@log_middleware(log_params=True, log_result=False)
async def __call__(self, handler, event, data):
# Логирует только ошибки, вход/выход в DEBUG режиме
@log_utility - декоратор для служебных функций
@log_utility
def _has_attachments(message: Message) -> bool:
# Логирует только ошибки
3. Контекстное логирование
LoggingContext - контекстное логирование с дополнительной информацией
context = get_logging_context(__name__)
context.add_context("user_id", user_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)
Покрытие логированием
1. Запуск и инициализация
- ✅ Инициализация бота
- ✅ Инициализация диспетчера
- ✅ Инициализация базы данных
- ✅ Регистрация обработчиков
- ✅ Уведомления администраторов
2. База данных (CRUD операции)
- ✅ Создание пользователей
- ✅ Создание вопросов
- ✅ Обновление вопросов
- ✅ Инициализация БД
3. Обработчики команд
- ✅ Команда /start
- ✅ Обработка deep links
- ✅ Создание/обновление пользователей
4. Бизнес-логика
- ✅ Отправка ответов авторам
- ✅ Обработка ошибок
- ✅ Валидация данных
5. Системные события
- ✅ Ошибки в обработчиках
- ✅ Очистка ресурсов
- ✅ Остановка бота
6. FSM состояния
- ✅ Переходы между состояниями
- ✅ Обработка FSM событий
- ✅ Отслеживание пользовательских сессий
Использование в коде
Импорт логгера и декораторов
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__)
Примеры использования декораторов
# Бизнес-события
@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
Контекстное логирование
# Создание контекста
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)
Традиционное логирование
# Информационные сообщения
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
CMD ["python", "main.py"]
# Просмотр логов в 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- статус операции
Примеры оптимизированного логирования
# 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
Мониторинг
Ключевые метрики для мониторинга
-
Запуск/остановка бота
🚀 Запуск бота в режиме polling🛑 Бот остановлен
-
Пользователи
👤 Создание пользователя👤 Обновление существующего пользователя
-
Вопросы и ответы
❓ Создание вопроса📝 Обновление вопроса📤 Отправка ответа
-
Ошибки
💥 Ошибка в обработчике⚠️ Предупреждения
Алерты
Рекомендуется настроить алерты на:
- ERROR уровень логов
- Отсутствие логов более 5 минут
- Частые ошибки в обработчиках
Настройка уровней
Уровень логирования настраивается через переменную окружения:
# В .env файле
DEBUG=true # DEBUG уровень
DEBUG=false # INFO уровень (по умолчанию)
Производительность
- Логирование асинхронное
- Минимальное влияние на производительность
- Эмодзи в логах для быстрого визуального поиска
- Структурированный формат для парсинга
🤝 Вклад в проект
- Fork репозитория
- Создайте feature branch
- Внесите изменения
- Создайте Pull Request
📄 Лицензия
MIT License
🆘 Поддержка
Если у вас возникли проблемы:
- Проверьте логи бота
- Убедитесь в правильности настройки
.env - Проверьте права доступа к файлам
- Обратитесь к администратору
Дополнительная документация:
- Руководство по суперпользователям - подробное описание системы ролей и функционала для суперпользователей
- Настройка Dependency Injection - подробное руководство по системе инъекции зависимостей
- Примеры использования DI - практические примеры использования системы инъекции зависимостей
📈 Prometheus метрики
AnonBot поддерживает экспорт метрик в формате Prometheus для мониторинга и анализа производительности.
Эндпоинты
- http://localhost:8081/metrics - экспорт метрик Prometheus
- http://localhost:8081/health - проверка здоровья бота
- http://localhost:8081/ready - готовность к работе (readiness probe)
- http://localhost:8081/status - информация о процессе (PID, uptime, использование ресурсов)
- 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):
scrape_configs:
- job_name: 'anon-bot'
static_configs:
- targets: ['localhost:8081']
metrics_path: '/metrics'
scrape_interval: 30s
Примеры запросов PromQL
Количество сообщений в секунду
rate(anon_bot_messages_total[5m])
Количество ошибок в секунду
rate(anon_bot_errors_total[5m])
Время обработки сообщений (95-й процентиль)
histogram_quantile(0.95, rate(anon_bot_message_processing_seconds_bucket[5m]))
Количество активных пользователей
anon_bot_active_users
Мониторинг в Docker
При запуске в Docker убедитесь, что порт 8081 доступен:
ports:
- "8081:8081"
Алерты
Рекомендуется настроить алерты на:
-
Высокий уровень ошибок
rate(anon_bot_errors_total[5m]) > 0.1 -
Медленную обработку
histogram_quantile(0.95, rate(anon_bot_message_processing_seconds_bucket[5m])) > 5 -
Недоступность сервиса
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 - соединения с БД
🚀 Развертывание исправлений
-
Перезапустите AnonBot:
docker-compose restart anon-bot -
Проверьте логи:
docker-compose logs -f anon-botДолжны появиться сообщения:
📊 Запуск обновления метрик... 📊 MetricsUpdater запущен с интервалом 30 секунд -
Проверьте метрики:
curl http://localhost:8081/metrics | grep anon_bot_active -
Проверьте в Grafana:
- Откройте дашборды AnonBot
- Дождитесь обновления данных (до 30 секунд)
- Проверьте отображение метрик
🔧 Дополнительные настройки
Изменение интервала обновления метрик
В файле bot.py можно изменить интервал обновления:
# Текущий интервал: 30 секунд
await start_metrics_updater(update_interval=30)
# Для более частого обновления (например, 10 секунд):
await start_metrics_updater(update_interval=10)
Добавление метрик БД в CRUD операции
Для автоматического сбора метрик БД в CRUD операциях добавьте декоратор:
from services.infrastructure import track_db_operation
@track_db_operation("SELECT", "users")
async def get_user(self, user_id: int):
# код метода
pass
📈 Мониторинг
После внесения исправлений рекомендуется настроить алерты:
- Низкая активность пользователей:
anon_bot_active_users < 1 - Много активных вопросов:
anon_bot_active_questions > 100 - Проблемы с БД:
anon_bot_db_connections_active == 0 - Высокое время ответа БД:
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с переданным путем к БД
Метрики не обновляются
-
Проверьте логи AnonBot:
docker-compose logs anon-bot | grep -i metrics -
Проверьте доступность эндпоинта:
curl http://localhost:8081/metrics -
Проверьте конфигурацию Prometheus:
curl http://localhost:9090/api/v1/targets | grep anon-bot
Ошибки в логах
Если появляются ошибки типа "Database connection failed", проверьте:
- Доступность базы данных
- Правильность пути к БД в конфигурации
- Права доступа к файлу БД
Нулевые значения в дашбордах
Если метрики отображаются, но имеют нулевые значения:
- Убедитесь, что в БД есть данные (пользователи, вопросы)
- Проверьте SQL запросы в
MetricsUpdater - Увеличьте интервал обновления для накопления данных
🐳 Docker
Сборка образа
docker build -t anon-bot .
Запуск контейнера
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
Управление контейнером
# Просмотр логов
docker logs anon-bot
# Остановка контейнера
docker stop anon-bot
# Запуск контейнера
docker start anon-bot
# Удаление контейнера
docker rm anon-bot
# Перезапуск контейнера
docker restart anon-bot
Переменные окружения
Все переменные окружения из .env_example можно передать через -e:
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:
# Проверка статуса
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 файл:
# Окружение для 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
Ручное использование
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
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="Привет!"
)
Ручное использование валидатора
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 осуществляется через админскую панель:
- Перейти в админку:
/admin - Нажать кнопку "🚦 Rate Limiting"
- Выбрать нужное действие:
- 📊 Статистика Rate Limiting - показывает подробную статистику:
- Общее количество запросов
- Процент успеха/ошибок
- Количество RetryAfter ошибок
- Среднее время ожидания
- 🔄 Сбросить статистику - сбрасывает всю статистику rate limiting
- ⚙️ Адаптировать конфигурацию - адаптирует настройки на основе текущей производительности
- 📊 Статистика Rate Limiting - показывает подробную статистику:
Важно: Доступ к функциям rate limiting имеют только администраторы бота (не суперпользователи).
Мониторинг
Логирование
Система логирует:
- Rate limiting события (DEBUG уровень)
- RetryAfter ошибки (WARNING уровень)
- Критические ошибки (ERROR уровень)
Статистика
RateLimitService отслеживает:
- Общее количество запросов
- Успешные/неудачные запросы
- Типы ошибок (RetryAfter, API ошибки)
- Время ожидания
Адаптивная конфигурация
Система автоматически адаптирует настройки:
- При >10% ошибок: ужесточает ограничения
- При <1% ошибок: ослабляет ограничения
- Требует минимум 100 запросов для адаптации
Интеграция с DI
Rate limiting полностью интегрирован в систему инъекции зависимостей:
# В обработчиках
async def some_handler(
message: Message,
rate_limit_service: RateLimitService
):
# rate_limit_service автоматически инжектируется
pass
Архитектурные особенности
Соблюдение принципов
- Single Responsibility: Каждый компонент отвечает за свою задачу
- Open/Closed: Легко расширяется новыми типами ограничений
- Dependency Inversion: Зависит от абстракций, а не от конкретных реализаций
Производительность
- Минимальные накладные расходы
- Эффективное управление памятью
- Асинхронная обработка
Надежность
- Обработка всех типов ошибок Telegram API
- Экспоненциальная задержка при повторных попытках
- Автоматическое восстановление после ошибок
Устранение неполадок
Высокий процент ошибок
- Проверьте статистику:
/ratelimit_stats - Адаптируйте конфигурацию:
/adapt_ratelimit - При необходимости ужесточите настройки в
.env
Медленная отправка сообщений
- Проверьте настройки
RATE_LIMIT_MESSAGES_PER_SECOND - Увеличьте значение для более быстрой отправки
- Убедитесь, что не превышаете лимиты Telegram API
Проблемы с интеграцией
-
Убедитесь, что middleware зарегистрирован в
loader.py -
Ошибка
TypeError: got an unexpected keyword argument 'dispatcher':- Причина: aiogram 3.x передает
dispatcherво все обработчики сообщений, но@inject_allне обрабатывает его правильно - Решение: Используйте специализированные декораторы вместо
@inject_all:# ❌ Неправильно @inject_all async def process_question(message: Message, dispatcher, ...): # ✅ Правильно @inject_question_services async def process_question(message: Message, question_service: QuestionService, ...): - Преимущества: Нет проблем с
dispatcher, меньше зависимостей, лучшая производительность
- Причина: aiogram 3.x передает
-
Проверьте, что все зависимости корректно импортированы
-
Проверьте логи на наличие ошибок инициализации
🔮 Планы развития
- Система суперпользователей - расширенные права для модерации с отображением информации об авторах
- Система инъекции зависимостей - современная архитектура с DI для лучшей тестируемости
- Система разрешений - гибкая система разрешений с соблюдением принципа OCP
- Prometheus метрики - мониторинг производительности и ошибок
- Rate limiting - защита от спама и DDoS с автоматической адаптацией
- Реорганизация структуры проекта - улучшенная архитектура с логической группировкой сервисов
- Локальная нумерация вопросов - каждый пользователь видит свои вопросы с номерами #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
- Интуитивная нумерация: Пользователи видят последовательные номера своих вопросов
- Консистентность: Номера в тексте и на кнопках всегда совпадают
- Автоматизация: Никаких ручных действий для поддержания нумерации