Merge branch 'v2_functions'
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
209
docs/ACTIVITY_TRACKING.md
Normal file
209
docs/ACTIVITY_TRACKING.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# Система отслеживания активности пользователей
|
||||
|
||||
## Описание
|
||||
|
||||
Система автоматически отслеживает активность пользователей и блокирует неактивных более 30 дней для исключения из рассылок.
|
||||
|
||||
## Компоненты
|
||||
|
||||
### 1. База данных
|
||||
|
||||
**Новое поле в таблице `users`:**
|
||||
- `last_activity` - дата и время последней активности пользователя
|
||||
- Автоматически обновляется при каждом взаимодействии с ботом
|
||||
|
||||
**Миграция:**
|
||||
- Файл: `migrations/versions/20260215_1201_08_1f1631301809_add_last_activity_to_users.py`
|
||||
- Добавляет поле `last_activity` и заполняет его значением `created_at` для существующих пользователей
|
||||
|
||||
### 2. ActivityService
|
||||
|
||||
**Файл:** `src/core/activity_service.py`
|
||||
|
||||
**Основные методы:**
|
||||
|
||||
- `update_user_activity(session, telegram_id)` - обновить последнюю активность пользователя
|
||||
- `get_inactive_users(session, days=30)` - получить список неактивных пользователей
|
||||
- `mark_inactive_users(session, days=30)` - пометить неактивных как заблокированных
|
||||
- `reactivate_user(session, telegram_id)` - реактивировать пользователя при новой активности
|
||||
- `check_and_mark_inactive_users()` - проверка для планировщика
|
||||
|
||||
**Параметры:**
|
||||
- `INACTIVITY_PERIOD_DAYS = 30` - период неактивности по умолчанию
|
||||
|
||||
### 3. ActivityMiddleware
|
||||
|
||||
**Файл:** `src/middlewares/activity.py`
|
||||
|
||||
Автоматически:
|
||||
- Обновляет `last_activity` при каждом сообщении или callback
|
||||
- Реактивирует пользователей, помеченных как неактивные
|
||||
|
||||
### 4. Планировщик задач
|
||||
|
||||
**Файл:** `src/core/scheduler.py`
|
||||
|
||||
**Расписание:**
|
||||
- Проверка неактивных пользователей: каждый день в 03:00
|
||||
|
||||
**Задачи:**
|
||||
- `check_inactive_users` - находит и блокирует неактивных пользователей
|
||||
|
||||
### 5. Интеграция с рассылками
|
||||
|
||||
**Модификации в `broadcast_services.py`:**
|
||||
```python
|
||||
# При получении списка пользователей для рассылки
|
||||
# автоматически исключаются все заблокированные,
|
||||
# включая заблокированных за неактивность (error_type='inactive')
|
||||
```
|
||||
|
||||
### 6. Админ-панель
|
||||
|
||||
**Новая секция "Неактивные пользователи":**
|
||||
|
||||
**Доступ:** Админ-панель → Массовая рассылка → ⏰ Неактивные пользователи
|
||||
|
||||
**Функции:**
|
||||
- Просмотр статистики неактивных пользователей
|
||||
- Количество заблокированных за неактивность
|
||||
- Список первых 10 неактивных с указанием дней неактивности
|
||||
- Кнопка "🔄 Проверить сейчас" - запуск проверки вручную
|
||||
|
||||
## Логика работы
|
||||
|
||||
### Отслеживание активности
|
||||
|
||||
1. Пользователь отправляет сообщение или нажимает callback
|
||||
2. `ActivityMiddleware` перехватывает событие
|
||||
3. Обновляется `last_activity` в базе данных
|
||||
4. Если пользователь был заблокирован за неактивность - реактивируется
|
||||
|
||||
### Автоматическая блокировка
|
||||
|
||||
1. Каждый день в 03:00 запускается задача `check_inactive_users`
|
||||
2. Система находит пользователей с `last_activity > 30 дней`
|
||||
3. Для каждого создается запись в `blocked_users`:
|
||||
- `error_type = 'inactive'`
|
||||
- `error_message = 'User inactive for 30 days'`
|
||||
- `is_active = True`
|
||||
4. Эти пользователи исключаются из будущих рассылок
|
||||
|
||||
### Реактивация
|
||||
|
||||
1. Неактивный пользователь снова взаимодействует с ботом
|
||||
2. `ActivityMiddleware` обновляет `last_activity`
|
||||
3. Запись в `blocked_users` деактивируется (`is_active = False`)
|
||||
4. Пользователь снова получит рассылки
|
||||
|
||||
## Настройка
|
||||
|
||||
### Изменение периода неактивности
|
||||
|
||||
В файле `src/core/activity_service.py`:
|
||||
|
||||
```python
|
||||
class ActivityService:
|
||||
# Изменить количество дней
|
||||
INACTIVITY_PERIOD_DAYS = 30 # Например, 60 дней
|
||||
```
|
||||
|
||||
### Изменение времени проверки
|
||||
|
||||
В файле `src/core/scheduler.py`:
|
||||
|
||||
```python
|
||||
self.scheduler.add_job(
|
||||
self._check_inactive_users,
|
||||
trigger=CronTrigger(hour=3, minute=0), # Изменить час и минуты
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
## Требования
|
||||
|
||||
**Добавлена зависимость:**
|
||||
- `apscheduler==3.10.4` в `requirements.txt`
|
||||
|
||||
## Логирование
|
||||
|
||||
Все действия системы логируются:
|
||||
- Обновление активности пользователей
|
||||
- Пометка неактивных пользователей
|
||||
- Реактивация пользователей
|
||||
- Запуск и остановка планировщика
|
||||
|
||||
**Примеры логов:**
|
||||
```
|
||||
INFO - Пользователь 123456789 помечен как неактивный (последняя активность: 2026-01-15)
|
||||
INFO - Пользователь 123456789 реактивирован
|
||||
INFO - Проверка неактивных пользователей завершена. Помечено: 5
|
||||
```
|
||||
|
||||
## База данных
|
||||
|
||||
### Структура BlockedUser для неактивных
|
||||
|
||||
```sql
|
||||
INSERT INTO blocked_users (
|
||||
telegram_id,
|
||||
error_type,
|
||||
error_message,
|
||||
is_active
|
||||
) VALUES (
|
||||
123456789,
|
||||
'inactive',
|
||||
'User inactive for 30 days',
|
||||
true
|
||||
);
|
||||
```
|
||||
|
||||
## Тестирование
|
||||
|
||||
### Ручной запуск проверки
|
||||
|
||||
1. Зайти в Админ-панель
|
||||
2. Массовая рассылка → Неактивные пользователи
|
||||
3. Нажать "🔄 Проверить сейчас"
|
||||
4. Система покажет количество помеченных пользователей
|
||||
|
||||
### Проверка middleware
|
||||
|
||||
Отправьте любое сообщение боту или нажмите callback - поле `last_activity` должно обновиться в БД.
|
||||
|
||||
### SQL запросы для проверки
|
||||
|
||||
```sql
|
||||
-- Неактивные более 30 дней
|
||||
SELECT * FROM users
|
||||
WHERE last_activity < NOW() - INTERVAL '30 days'
|
||||
AND is_registered = true;
|
||||
|
||||
-- Заблокированные за неактивность
|
||||
SELECT * FROM blocked_users
|
||||
WHERE error_type = 'inactive'
|
||||
AND is_active = true;
|
||||
|
||||
-- Проверка last_activity
|
||||
SELECT telegram_id, username, first_name, last_activity
|
||||
FROM users
|
||||
ORDER BY last_activity DESC
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
## Преимущества
|
||||
|
||||
1. **Автоматизация** - не требует ручного вмешательства
|
||||
2. **Гибкость** - легко настроить период неактивности
|
||||
3. **Реактивация** - пользователи автоматически возвращаются в рассылки при активности
|
||||
4. **Контроль** - админ может видеть статистику и запускать проверку вручную
|
||||
5. **Оптимизация** - не отправляются сообщения неактивным пользователям
|
||||
6. **Логирование** - все действия фиксируются
|
||||
|
||||
## Возможные улучшения
|
||||
|
||||
1. Настраиваемый период через админ-панель
|
||||
2. Email уведомления администратору о заблокированных пользователях
|
||||
3. Отправка уведомления пользователю перед блокировкой
|
||||
4. Разные периоды для разных типов пользователей
|
||||
5. Статистика активности по дням/неделям/месяцам
|
||||
270
docs/BROADCAST_SYSTEM.md
Normal file
270
docs/BROADCAST_SYSTEM.md
Normal file
@@ -0,0 +1,270 @@
|
||||
# Система рассылок с Redis очередями
|
||||
|
||||
## Обзор
|
||||
|
||||
Расширенная система массовых рассылок с поддержкой трех типов рассылки:
|
||||
- **ЛС пользователям** - массовая рассылка по личным сообщениям с отслеживанием заблокированных
|
||||
- **В канал** - отправка в Telegram канал
|
||||
- **В группу** - отправка в Telegram группу
|
||||
|
||||
## Основные возможности
|
||||
|
||||
### 1. Рассылка в личные сообщения
|
||||
|
||||
**Особенности:**
|
||||
- Использование Redis очередей для управления потоком сообщений
|
||||
- Автоматическое отслеживание пользователей, заблокировавших бота
|
||||
- Пакетная отправка с задержками для соблюдения лимитов Telegram
|
||||
- Детальная обработка ошибок (блокировка, деактивация аккаунта, etc.)
|
||||
- Автоматическое повторение при FloodWait ошибках
|
||||
|
||||
**Технические детали:**
|
||||
- Размер пакета: 30 сообщений
|
||||
- Задержка между пакетами: 1 секунда
|
||||
- Дополнительная задержка при FloodWait: 5 секунд + время из ошибки
|
||||
|
||||
### 2. Рассылка в канал/группу
|
||||
|
||||
**Особенности:**
|
||||
- Управление списком каналов и групп через админ-панель
|
||||
- Проверка прав бота перед добавлением канала
|
||||
- Возможность добавить описание для каждого канала
|
||||
- Активация/деактивация каналов
|
||||
|
||||
## Архитектура
|
||||
|
||||
### Модели данных
|
||||
|
||||
#### BroadcastChannel
|
||||
Хранит информацию о каналах и группах для рассылки:
|
||||
- `chat_id` - ID чата в Telegram
|
||||
- `chat_type` - тип (channel/group)
|
||||
- `title` - название
|
||||
- `username` - юзернейм (если есть)
|
||||
- `description` - описание
|
||||
- `is_active` - активен ли для рассылок
|
||||
- `added_by` - кто добавил
|
||||
|
||||
#### BlockedUser
|
||||
Отслеживание заблокированных/недоступных пользователей:
|
||||
- `telegram_id` - ID пользователя
|
||||
- `error_type` - тип ошибки (blocked_bot, deactivated, not_found, etc.)
|
||||
- `error_message` - полное сообщение об ошибке
|
||||
- `first_blocked_at` - первая попытка
|
||||
- `last_attempt_at` - последняя попытка
|
||||
- `attempt_count` - количество неудачных попыток
|
||||
- `is_active` - активна ли блокировка
|
||||
|
||||
#### BroadcastLog
|
||||
История рассылок:
|
||||
- `broadcast_type` - тип (direct/channel/group)
|
||||
- `target_id` - ID канала/группы (для соответствующих типов)
|
||||
- `message_type` - тип сообщения
|
||||
- `message_text` - текст
|
||||
- `file_id` - ID файла (если есть)
|
||||
- Статистика: `total_recipients`, `success_count`, `failed_count`, `blocked_count`
|
||||
- `created_by` - кто запустил
|
||||
- `started_at`, `completed_at` - временные метки
|
||||
- `status` - статус (pending/in_progress/completed/failed)
|
||||
|
||||
### Сервисы
|
||||
|
||||
#### BroadcastService
|
||||
Основной сервис для рассылок (`src/core/broadcast_services.py`):
|
||||
|
||||
**Методы:**
|
||||
- `broadcast_to_users()` - рассылка в ЛС
|
||||
- `broadcast_to_channel()` - отправка в канал/группу
|
||||
- `send_message_to_user()` - отправка одному пользователю с обработкой ошибок
|
||||
- `check_user_blocked()` - проверка блокировки
|
||||
- `mark_user_blocked()` - отметить как заблокированного
|
||||
- `unblock_user()` - разблокировать
|
||||
|
||||
#### RedisQueue
|
||||
Класс для работы с Redis очередями:
|
||||
|
||||
**Методы:**
|
||||
- `connect()` - подключение к Redis
|
||||
- `disconnect()` - отключение
|
||||
- `add_to_queue()` - добавить в очередь
|
||||
- `get_from_queue()` - получить из очереди (блокирующая)
|
||||
- `get_queue_length()` - получить длину очереди
|
||||
- `clear_queue()` - очистить очередь
|
||||
|
||||
## Использование
|
||||
|
||||
### Добавление канала/группы
|
||||
|
||||
1. Перейдите в админ-панель → Массовая рассылка → Управление каналами
|
||||
2. Нажмите "Добавить канал/группу"
|
||||
3. Получите ID канала:
|
||||
- Добавьте бота в канал/группу как администратора
|
||||
- Перешлите сообщение из канала боту @userinfobot
|
||||
- Скопируйте ID чата (обычно отрицательное число)
|
||||
4. Отправьте ID боту
|
||||
5. При успешной проверке отправьте описание или /skip
|
||||
|
||||
### Создание рассылки
|
||||
|
||||
1. Перейдите в админ-панель → Массовая рассылка → Создать рассылку
|
||||
2. Выберите тип рассылки:
|
||||
- **ЛС пользователям** - всем зарегистрированным
|
||||
- **В канал** - выберите канал из списка
|
||||
- **В группу** - выберите группу из списка
|
||||
3. Отправьте сообщение (текст, фото, видео или документ)
|
||||
4. Дождитесь завершения и получите статистику
|
||||
|
||||
### Просмотр статистики
|
||||
|
||||
Перейдите в админ-панель → Массовая рассылка → Статистика:
|
||||
- Общее количество рассылок
|
||||
- Количество заблокированных пользователей
|
||||
- История последних 5 рассылок с детальной статистикой
|
||||
|
||||
## Обработка ошибок
|
||||
|
||||
Система автоматически обрабатывает следующие типы ошибок:
|
||||
|
||||
### TelegramForbiddenError
|
||||
Пользователь заблокировал бота. Помечается как `blocked_bot`.
|
||||
|
||||
### TelegramBadRequest
|
||||
- `user is deactivated` → `deactivated`
|
||||
- `user not found` → `not_found`
|
||||
- `chat not found` → `chat_not_found`
|
||||
- Остальные → `bad_request`
|
||||
|
||||
### TelegramRetryAfter (FloodWait)
|
||||
Автоматическая задержка и повторная попытка отправки.
|
||||
|
||||
### Другие ошибки
|
||||
Логируются как `unknown_error`.
|
||||
|
||||
## Конфигурация
|
||||
|
||||
### Переменные окружения
|
||||
|
||||
```env
|
||||
# Redis
|
||||
REDIS_URL=redis://localhost:6379/0 # По умолчанию
|
||||
```
|
||||
|
||||
### Docker Compose
|
||||
|
||||
Redis автоматически запускается при использовании docker-compose:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: lottery_redis
|
||||
restart: unless-stopped
|
||||
command: redis-server --appendonly yes
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
networks:
|
||||
- lottery_network
|
||||
```
|
||||
|
||||
### Настройки в коде
|
||||
|
||||
В `BroadcastService` (`src/core/broadcast_services.py`):
|
||||
|
||||
```python
|
||||
BATCH_SIZE = 30 # Сообщений в пакете
|
||||
BATCH_DELAY = 1.0 # Задержка между пакетами (секунды)
|
||||
RETRY_AFTER_DELAY = 5.0 # Дополнительная задержка при FloodWait
|
||||
```
|
||||
|
||||
## Миграция базы данных
|
||||
|
||||
Для применения новых таблиц:
|
||||
|
||||
```bash
|
||||
# Применить миграцию
|
||||
python -m alembic upgrade head
|
||||
|
||||
# Откатить миграцию
|
||||
python -m alembic downgrade -1
|
||||
```
|
||||
|
||||
## Мониторинг
|
||||
|
||||
### Логи
|
||||
|
||||
Все операции рассылки логируются:
|
||||
- Успешные отправки (уровень DEBUG)
|
||||
- Блокировки пользователей (уровень INFO)
|
||||
- FloodWait задержки (уровень WARNING)
|
||||
- Ошибки отправки (уровень ERROR)
|
||||
|
||||
### База данных
|
||||
|
||||
Проверка статистики через SQL:
|
||||
|
||||
```sql
|
||||
-- Количество заблокированных пользователей
|
||||
SELECT COUNT(*) FROM blocked_users WHERE is_active = true;
|
||||
|
||||
-- Статистика рассылок
|
||||
SELECT
|
||||
broadcast_type,
|
||||
COUNT(*) as total,
|
||||
SUM(success_count) as delivered,
|
||||
SUM(blocked_count) as blocked
|
||||
FROM broadcast_logs
|
||||
GROUP BY broadcast_type;
|
||||
```
|
||||
|
||||
## Рекомендации
|
||||
|
||||
1. **Перед запуском большой рассылки:**
|
||||
- Проверьте количество заблокированных пользователей
|
||||
- Убедитесь, что Redis работает
|
||||
- Проверьте логи на наличие ошибок
|
||||
|
||||
2. **При добавлении канала:**
|
||||
- Убедитесь, что бот добавлен как администратор
|
||||
- Проверьте, что бот имеет права на отправку сообщений
|
||||
|
||||
3. **Мониторинг производительности:**
|
||||
- Следите за временем выполнения рассылок
|
||||
- При необходимости увеличьте BATCH_SIZE (не более 40)
|
||||
- Уменьшите BATCH_DELAY при стабильной работе (не менее 0.5 сек)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Проблема: Рассылка зависает
|
||||
|
||||
**Решение:**
|
||||
1. Проверьте подключение к Redis
|
||||
2. Проверьте логи на наличие ошибок
|
||||
3. Убедитесь, что нет FloodWait ошибок
|
||||
|
||||
### Проблема: Не удается добавить канал
|
||||
|
||||
**Решение:**
|
||||
1. Убедитесь, что бот добавлен в канал/группу
|
||||
2. Проверьте права бота (должен быть администратором)
|
||||
3. Убедитесь, что ID правильный (должен быть отрицательным)
|
||||
|
||||
### Проблема: Высокий процент неудач при рассылке
|
||||
|
||||
**Решение:**
|
||||
1. Проверьте количество заблокированных пользователей в статистике
|
||||
2. Увеличьте BATCH_DELAY для снижения нагрузки
|
||||
3. Проверьте логи на частые FloodWait ошибки
|
||||
|
||||
## Безопасность
|
||||
|
||||
- Все операции рассылки доступны только администраторам
|
||||
- ID каналов/групп хранятся в зашифрованном виде (BigInteger)
|
||||
- История рассылок связана с администратором, который ее запустил
|
||||
- Автоматическое логирование всех операций
|
||||
|
||||
## Производительность
|
||||
|
||||
- Redis очереди обеспечивают асинхронную обработку
|
||||
- Пакетная отправка снижает нагрузку на API Telegram
|
||||
- Автоматическое управление задержками предотвращает FloodWait
|
||||
- Кэширование заблокированных пользователей ускоряет рассылку
|
||||
189
docs/UPDATES_2026_02_15.md
Normal file
189
docs/UPDATES_2026_02_15.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# Обновления от 15.02.2026
|
||||
|
||||
## 📋 Реализованные улучшения
|
||||
|
||||
### 1. 📊 Экспорт/Импорт в формате XLSX
|
||||
|
||||
**Что изменилось:**
|
||||
- Экспорт пользователей теперь создает файлы в формате **XLSX** (Excel) вместо JSON
|
||||
- Импорт пользователей принимает **XLSX файлы** вместо JSON
|
||||
|
||||
**Формат XLSX файла:**
|
||||
|
||||
#### Колонки в экспорте:
|
||||
1. `Telegram ID` - обязательная колонка для импорта
|
||||
2. `Username` - имя пользователя в Telegram
|
||||
3. `Имя` / `Фамилия` - реальные имя и фамилия
|
||||
4. `Никнейм` - отображаемое имя в боте
|
||||
5. `Телефон` - номер телефона
|
||||
6. `Клубная карта` - номер клубной карты
|
||||
7. `Зарегистрирован` - статус регистрации (Да/Нет)
|
||||
8. `Админ` - является ли админом (Да/Нет)
|
||||
9. `Код верификации` - код для подтверждения
|
||||
10. `Дата создания` - когда пользователь создан
|
||||
11. `Последняя активность` - последнее взаимодействие с ботом
|
||||
12. `Заблокирован в чате` - статус блокировки в чате
|
||||
|
||||
**Преимущества XLSX:**
|
||||
- ✅ Удобное редактирование в Excel/LibreOffice
|
||||
- ✅ Визуальный контроль данных
|
||||
- ✅ Авто-подбор ширины колонок
|
||||
- ✅ Цветное оформление заголовков
|
||||
- ✅ Легкая сортировка и фильтрация
|
||||
|
||||
**Безопасность:**
|
||||
- 🔒 Статус админа НЕ импортируется из файла (только ручное назначение)
|
||||
- 🔒 Все данные валидируются перед импортом
|
||||
|
||||
**Файлы:**
|
||||
- `requirements.txt` - добавлена библиотека `openpyxl==3.1.2`
|
||||
- `src/handlers/admin_panel.py` - обновлены функции экспорта/импорта
|
||||
|
||||
---
|
||||
|
||||
### 2. 💬 Обработка команд выхода в чате
|
||||
|
||||
**Что добавлено:**
|
||||
Теперь находясь в режиме чата можно быстро вернуться в главное меню, написав одну из команд:
|
||||
- `/start` - выход из чата в главное меню
|
||||
- `start` - выход из чата в главное меню
|
||||
- `старт` - выход из чата в главное меню
|
||||
- `/exit` - выход из чата (как и раньше)
|
||||
|
||||
**Как работает:**
|
||||
1. Пользователь в режиме чата (ChatStates.in_chat)
|
||||
2. Пишет одну из команд: `/start`, `start` или `старт`
|
||||
3. Автоматически выходит из чата
|
||||
4. Получает главное меню
|
||||
|
||||
**Преимущества:**
|
||||
- ⚡ Быстрый возврат в меню без кнопок
|
||||
- 🎯 Интуитивные команды (start/старт)
|
||||
- 🔄 Совместимость с привычным поведением ботов
|
||||
|
||||
**Файлы:**
|
||||
- `src/handlers/chat_handlers.py` - добавлена функция `check_exit_keywords`
|
||||
|
||||
---
|
||||
|
||||
### 3. ❓ Система справки
|
||||
|
||||
**Что добавлено:**
|
||||
Новая полноценная система помощи пользователям с интерактивной навигацией.
|
||||
|
||||
**Разделы справки:**
|
||||
|
||||
#### 📝 Регистрация
|
||||
- Пошаговая инструкция по регистрации
|
||||
- Какие данные нужны
|
||||
- Процесс одобрения администратором
|
||||
- Что дает регистрация
|
||||
|
||||
#### 🎰 Участие в розыгрышах
|
||||
- Как принять участие в розыгрыше
|
||||
- Что указано в описании розыгрыша
|
||||
- Как узнать о результатах
|
||||
- Что делать при выигрыше
|
||||
|
||||
#### 💬 Чат
|
||||
- Вход и выход из чата
|
||||
- Какие сообщения можно отправлять (текст, фото, видео, документы, стикеры)
|
||||
- Правила чата
|
||||
- Управление чатом для админов
|
||||
|
||||
#### ⚙️ Команды
|
||||
- Список всех доступных команд бота
|
||||
- Описание каждой команды
|
||||
- Для админов - дополнительные админские команды
|
||||
- Полезные советы по использованию
|
||||
|
||||
**Доступ к справке:**
|
||||
- Кнопка `❓ Справка` в главном меню
|
||||
- Команда `/help` в любой момент
|
||||
- Интерактивная навигация между разделами
|
||||
|
||||
**Особенности:**
|
||||
- 📱 Адаптивный контент (админы видят дополнительные команды)
|
||||
- 🔄 Удобная навигация между разделами
|
||||
- 🏠 Быстрый возврат в главное меню
|
||||
- 📖 Подробные инструкции с примерами
|
||||
|
||||
**Файлы:**
|
||||
- `src/handlers/help_handlers.py` - новый модуль справки (265 строк)
|
||||
- `src/components/ui.py` - добавлена кнопка "❓ Справка" в главное меню
|
||||
- `main.py` - зарегистрирован роутер справки
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Технические детали
|
||||
|
||||
### Зависимости
|
||||
```txt
|
||||
openpyxl==3.1.2 # Работа с Excel файлами
|
||||
```
|
||||
|
||||
### Новые файлы
|
||||
- `src/handlers/help_handlers.py` - система справки
|
||||
|
||||
### Обновленные файлы
|
||||
- `requirements.txt` - добавлена openpyxl
|
||||
- `main.py` - регистрация help_router
|
||||
- `src/handlers/admin_panel.py` - XLSX экспорт/импорт
|
||||
- `src/handlers/chat_handlers.py` - обработка ключевых слов
|
||||
- `src/components/ui.py` - кнопка справки в меню
|
||||
|
||||
### Роутеры
|
||||
```python
|
||||
# Порядок регистрации роутеров:
|
||||
1. main router (базовые команды)
|
||||
2. message_admin_router
|
||||
3. admin_router
|
||||
4. registration_router
|
||||
5. admin_account_router
|
||||
6. admin_chat_router
|
||||
7. redraw_router
|
||||
8. p2p_chat_router
|
||||
9. help_router # ← НОВЫЙ
|
||||
10. chat_router
|
||||
11. account_router
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Статистика изменений
|
||||
|
||||
- **Новые файлы:** 1 (help_handlers.py)
|
||||
- **Измененные файлы:** 4 (admin_panel.py, chat_handlers.py, ui.py, main.py)
|
||||
- **Новые зависимости:** 1 (openpyxl)
|
||||
- **Новые команды:** 1 (/help)
|
||||
- **Новые обработчики:** 6 (помощь + ключевые слова)
|
||||
- **Строк кода добавлено:** ~400
|
||||
|
||||
---
|
||||
|
||||
## ✅ Тестирование
|
||||
|
||||
### Проверено:
|
||||
- ✅ Бот успешно запускается
|
||||
- ✅ Контейнер пересобран с новыми зависимостями
|
||||
- ✅ Справка доступна из главного меню
|
||||
- ✅ Кнопка "❓ Справка" работает (подтверждено логами)
|
||||
- ✅ Обработчик help_main вызывается корректно
|
||||
- ✅ Нет ошибок компиляции в новом коде
|
||||
|
||||
### Требуется протестировать:
|
||||
- ⏳ Экспорт пользователей в XLSX
|
||||
- ⏳ Импорт пользователей из XLSX
|
||||
- ⏳ Обработка команд start/старт в чате
|
||||
- ⏳ Навигация по всем разделам справки
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Итоги
|
||||
|
||||
Все три запрошенные функции успешно реализованы:
|
||||
1. ✅ **XLSX экспорт/импорт** - удобная работа с данными пользователей
|
||||
2. ✅ **Обработка start в чате** - быстрый возврат в главное меню
|
||||
3. ✅ **Система справки** - полноценная помощь для пользователей
|
||||
|
||||
Бот готов к использованию новых функций! 🚀
|
||||
470
docs/USER_MANAGEMENT_GUIDE.md
Normal file
470
docs/USER_MANAGEMENT_GUIDE.md
Normal file
@@ -0,0 +1,470 @@
|
||||
# Система управления пользователями
|
||||
|
||||
## Обзор
|
||||
|
||||
Система управления пользователями предоставляет администраторам инструменты для:
|
||||
- Поиска пользователей по различным критериям
|
||||
- Просмотра детальной информации о пользователях
|
||||
- Блокировки/разблокировки пользователей в чате
|
||||
- Управления большими базами пользователей (1000+)
|
||||
|
||||
## Функциональность
|
||||
|
||||
### 1. Блокировка в чате
|
||||
|
||||
**Назначение:** Запрет отправки сообщений в чат бота для конкретного пользователя.
|
||||
|
||||
**Как работает:**
|
||||
- Заблокированный пользователь не может отправлять сообщения в P2P чат
|
||||
- Блокировка не затрагивает другие функции бота (участие в розыгрышах, просмотр статистики)
|
||||
- Блокировка фиксируется в поле `is_chat_banned` в базе данных
|
||||
|
||||
**Применение:**
|
||||
1. Найти пользователя через поиск или список
|
||||
2. Открыть карточку пользователя
|
||||
3. Нажать кнопку "🚫 Заблокировать в чате"
|
||||
|
||||
### 2. Поиск пользователей
|
||||
|
||||
**Критерии поиска:**
|
||||
- Username (с @ или без)
|
||||
- Имя или фамилия
|
||||
- Telegram ID
|
||||
- Номер клубной карты
|
||||
- Никнейм
|
||||
|
||||
**Пример запросов:**
|
||||
- `@username` - поиск по username
|
||||
- `Иван` - поиск по имени/фамилии
|
||||
- `123456789` - поиск по Telegram ID или номеру карты
|
||||
- `nickname` - поиск по никнейму
|
||||
|
||||
**Особенности:**
|
||||
- Поиск работает по принципу "ИЛИ" (OR) - ищет во всех полях одновременно
|
||||
- Поддержка частичного совпадения (ILIKE)
|
||||
- Автоматическая пагинация (15 пользователей на странице)
|
||||
|
||||
### 3. Списки пользователей
|
||||
|
||||
#### Все пользователи
|
||||
- Отображаются все зарегистрированные в боте
|
||||
- Сортировка по дате создания (новые первые)
|
||||
- Пагинация по 15 пользователей
|
||||
|
||||
#### Заблокированные пользователи
|
||||
- Отображаются только пользователи с блокировкой в чате
|
||||
- Быстрый доступ для управления блокировками
|
||||
|
||||
### 4. Карточка пользователя
|
||||
|
||||
**Отображаемая информация:**
|
||||
- **Основное:**
|
||||
- Имя и фамилия
|
||||
- Username (если есть)
|
||||
- Telegram ID
|
||||
|
||||
- **Статистика:**
|
||||
- Дата регистрации
|
||||
- Последняя активность
|
||||
- Статус регистрации
|
||||
- Статус администратора
|
||||
|
||||
- **Дополнительно:**
|
||||
- Никнейм (если установлен)
|
||||
- Номер клубной карты (если привязан)
|
||||
- Телефон (если указан)
|
||||
- Статус блокировки в чате
|
||||
|
||||
## Архитектура
|
||||
|
||||
### Компоненты системы
|
||||
|
||||
#### 1. UserManagementService
|
||||
**Файл:** `src/core/user_management.py`
|
||||
|
||||
**Методы:**
|
||||
```python
|
||||
# Поиск с фильтрами и пагинацией
|
||||
async def search_users(
|
||||
session: AsyncSession,
|
||||
query: str = None, # Поисковый запрос
|
||||
page: int = 1, # Номер страницы
|
||||
per_page: int = 15, # Размер страницы
|
||||
filters: Dict = None # Фильтры (is_registered, is_admin, is_chat_banned)
|
||||
) -> Tuple[List[User], int]:
|
||||
"""Возвращает (список пользователей, общее количество)"""
|
||||
|
||||
# Получение по ID
|
||||
async def get_user_by_id(session: AsyncSession, user_id: int) -> Optional[User]:
|
||||
"""Получить пользователя по внутреннему ID"""
|
||||
|
||||
# Блокировка в чате
|
||||
async def ban_user_in_chat(session: AsyncSession, user_id: int) -> bool:
|
||||
"""Заблокировать пользователя в чате"""
|
||||
|
||||
# Разблокировка в чате
|
||||
async def unban_user_in_chat(session: AsyncSession, user_id: int) -> bool:
|
||||
"""Разблокировать пользователя в чате"""
|
||||
|
||||
# Статистика
|
||||
async def get_user_stats(session: AsyncSession) -> Dict[str, int]:
|
||||
"""Получить статистику: total, registered, admins, chat_banned"""
|
||||
|
||||
# Форматирование
|
||||
def format_user_info(user: User, detailed: bool = False) -> str:
|
||||
"""Красивый HTML вывод информации о пользователе"""
|
||||
```
|
||||
|
||||
**Константы:**
|
||||
- `USERS_PER_PAGE = 15` - количество пользователей на странице
|
||||
|
||||
#### 2. Обработчики админ-панели
|
||||
**Файл:** `src/handlers/admin_panel.py`
|
||||
|
||||
**Основные обработчики:**
|
||||
|
||||
```python
|
||||
# Главное меню управления пользователями
|
||||
@admin_router.callback_query(F.data == "admin_users")
|
||||
async def admin_users_menu(callback: CallbackQuery):
|
||||
"""Показывает статистику и главное меню"""
|
||||
|
||||
# Запрос поискового запроса
|
||||
@admin_router.callback_query(F.data == "admin_users_search")
|
||||
async def admin_users_search_prompt(callback: CallbackQuery, state: FSMContext):
|
||||
"""Переводит в режим ожидания поискового запроса"""
|
||||
|
||||
# Обработка поиска
|
||||
@admin_router.message(AdminStates.user_management_search)
|
||||
async def admin_users_search_process(message: Message, state: FSMContext):
|
||||
"""Выполняет поиск и показывает результаты"""
|
||||
|
||||
# Список всех пользователей
|
||||
@admin_router.callback_query(F.data.startswith("admin_users_list:"))
|
||||
async def admin_users_list(callback: CallbackQuery):
|
||||
"""Постраничный список всех пользователей"""
|
||||
|
||||
# Список заблокированных
|
||||
@admin_router.callback_query(F.data.startswith("admin_users_banned:"))
|
||||
async def admin_users_banned_list(callback: CallbackQuery):
|
||||
"""Список только заблокированных пользователей"""
|
||||
|
||||
# Просмотр пользователя
|
||||
@admin_router.callback_query(F.data.startswith("admin_user_view:"))
|
||||
async def admin_user_view(callback: CallbackQuery):
|
||||
"""Детальная карточка пользователя"""
|
||||
|
||||
# Блокировка
|
||||
@admin_router.callback_query(F.data.startswith("admin_user_ban:"))
|
||||
async def admin_user_ban(callback: CallbackQuery):
|
||||
"""Заблокировать пользователя"""
|
||||
|
||||
# Разблокировка
|
||||
@admin_router.callback_query(F.data.startswith("admin_user_unban:"))
|
||||
async def admin_user_unban(callback: CallbackQuery):
|
||||
"""Разблокировать пользователя"""
|
||||
```
|
||||
|
||||
#### 3. FSM состояния
|
||||
**Файл:** `src/handlers/admin_panel.py`
|
||||
|
||||
```python
|
||||
class AdminStates(StatesGroup):
|
||||
# ... другие состояния ...
|
||||
user_management_search = State() # Ожидание поискового запроса
|
||||
user_management_view = State() # Просмотр пользователя
|
||||
```
|
||||
|
||||
### Модель данных
|
||||
|
||||
#### Поле is_chat_banned
|
||||
**Файл:** `src/core/models.py`
|
||||
|
||||
```python
|
||||
class User(Base):
|
||||
# ... другие поля ...
|
||||
is_chat_banned: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
last_activity: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
```
|
||||
|
||||
**Миграция:** `migrations/versions/20260215_0403_00_b4c435a7dc5f_add_is_chat_banned_to_users.py`
|
||||
|
||||
### Интеграция с чатом
|
||||
|
||||
**Файл:** `src/core/chat_services.py`
|
||||
|
||||
```python
|
||||
class ChatPermissionService:
|
||||
@staticmethod
|
||||
async def can_send_message(session: AsyncSession, telegram_id: int) -> Tuple[bool, str]:
|
||||
"""
|
||||
Проверяет, может ли пользователь отправлять сообщения в чат
|
||||
|
||||
Порядок проверок:
|
||||
1. Глобальная блокировка (BanService)
|
||||
2. Блокировка в чате (is_chat_banned)
|
||||
3. Регистрация в боте
|
||||
|
||||
Returns:
|
||||
(может_отправить: bool, причина_если_нет: str)
|
||||
"""
|
||||
```
|
||||
|
||||
**Проверка в обработчиках P2P сообщений:**
|
||||
```python
|
||||
can_send, reason = await ChatPermissionService.can_send_message(session, message.from_user.id)
|
||||
if not can_send:
|
||||
await message.answer(f"❌ {reason}")
|
||||
return
|
||||
```
|
||||
|
||||
## Производительность
|
||||
|
||||
### Оптимизация для больших баз (1000+ пользователей)
|
||||
|
||||
#### 1. Пагинация
|
||||
- Все списки ограничены 15 пользователями на странице
|
||||
- SQL LIMIT/OFFSET для эффективных запросов
|
||||
- Подсчет общего количества отдельным запросом
|
||||
|
||||
#### 2. Индексы (рекомендуется добавить)
|
||||
```sql
|
||||
-- Для поиска по username
|
||||
CREATE INDEX idx_users_username ON users(username);
|
||||
|
||||
-- Для поиска по имени/фамилии
|
||||
CREATE INDEX idx_users_names ON users(first_name, last_name);
|
||||
|
||||
-- Для поиска по клубной карте
|
||||
CREATE INDEX idx_users_card ON users(club_card_number);
|
||||
|
||||
-- Для фильтрации заблокированных
|
||||
CREATE INDEX idx_users_chat_banned ON users(is_chat_banned) WHERE is_chat_banned = true;
|
||||
```
|
||||
|
||||
#### 3. ILIKE оптимизация
|
||||
Для больших баз рекомендуется добавить индекс с расширением pg_trgm:
|
||||
```sql
|
||||
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||||
CREATE INDEX idx_users_username_trgm ON users USING gin(username gin_trgm_ops);
|
||||
CREATE INDEX idx_users_names_trgm ON users USING gin((first_name || ' ' || last_name) gin_trgm_ops);
|
||||
```
|
||||
|
||||
#### 4. Кеширование (будущее улучшение)
|
||||
- Кеш для статистики (Redis)
|
||||
- Кеш для часто просматриваемых пользователей
|
||||
- TTL: 5 минут
|
||||
|
||||
## Использование
|
||||
|
||||
### Для администраторов
|
||||
|
||||
#### Шаг 1: Доступ к управлению
|
||||
1. Открыть админ-панель: `/admin`
|
||||
2. Выбрать "👤 Управление пользователями"
|
||||
|
||||
#### Шаг 2: Поиск пользователя
|
||||
**Вариант A: Быстрый поиск**
|
||||
1. Нажать "🔍 Поиск пользователей"
|
||||
2. Ввести запрос (username, имя, ID)
|
||||
3. Выбрать пользователя из результатов
|
||||
|
||||
**Вариант B: Просмотр списка**
|
||||
1. Нажать "📋 Все пользователи"
|
||||
2. Перемещаться по страницам (⬅️ Назад / ➡️ Далее)
|
||||
3. Выбрать пользователя
|
||||
|
||||
**Вариант C: Только заблокированные**
|
||||
1. Нажать "🚫 Заблокированные"
|
||||
2. Просмотреть список заблокированных
|
||||
3. Выбрать для разблокировки
|
||||
|
||||
#### Шаг 3: Управление блокировкой
|
||||
1. В карточке пользователя нажать:
|
||||
- "🚫 Заблокировать в чате" - для блокировки
|
||||
- "✅ Разблокировать в чате" - для разблокировки
|
||||
2. Подтвердить действие
|
||||
|
||||
### Для разработчиков
|
||||
|
||||
#### Добавление новых фильтров
|
||||
|
||||
```python
|
||||
# В UserManagementService.search_users()
|
||||
if filters:
|
||||
if 'custom_field' in filters:
|
||||
conditions.append(User.custom_field == filters['custom_field'])
|
||||
```
|
||||
|
||||
#### Добавление новых полей в карточку
|
||||
|
||||
```python
|
||||
# В UserManagementService.format_user_info()
|
||||
if detailed:
|
||||
text += f"🆕 Кастомное поле: {user.custom_field}\n"
|
||||
```
|
||||
|
||||
#### Создание нового списка
|
||||
|
||||
```python
|
||||
@admin_router.callback_query(F.data.startswith("admin_users_custom:"))
|
||||
async def admin_users_custom_list(callback: CallbackQuery):
|
||||
page = int(callback.data.split(":")[1])
|
||||
|
||||
async with async_session_maker() as session:
|
||||
users, total = await UserManagementService.search_users(
|
||||
session,
|
||||
page=page,
|
||||
filters={'custom_field': True}
|
||||
)
|
||||
|
||||
# ... рендер списка ...
|
||||
```
|
||||
|
||||
## Мониторинг
|
||||
|
||||
### Логирование
|
||||
```python
|
||||
logger.info(f"Пользователь {user.telegram_id} заблокирован в чате")
|
||||
logger.error(f"Ошибка блокировки пользователя {user_id}: {e}")
|
||||
```
|
||||
|
||||
### Метрики для мониторинга
|
||||
- Количество поисковых запросов
|
||||
- Среднее время ответа на поиск
|
||||
- Количество блокировок/разблокировок
|
||||
- Топ-10 самых активных поисковых запросов
|
||||
|
||||
## Безопасность
|
||||
|
||||
### Проверка прав доступа
|
||||
Все обработчики проверяют права администратора:
|
||||
```python
|
||||
if not is_admin(callback.from_user.id):
|
||||
await callback.answer("❌ Доступ запрещен", show_alert=True)
|
||||
return
|
||||
```
|
||||
|
||||
### Защита от SQL-инъекций
|
||||
- Все запросы используют параметризованные запросы SQLAlchemy
|
||||
- Нет прямой конкатенации SQL
|
||||
|
||||
### Аудит действий (рекомендуется добавить)
|
||||
```sql
|
||||
CREATE TABLE admin_actions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
admin_telegram_id BIGINT NOT NULL,
|
||||
action VARCHAR(50) NOT NULL, -- 'ban_user', 'unban_user', 'search_user'
|
||||
target_user_id INTEGER,
|
||||
details JSONB,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
## Расширения
|
||||
|
||||
### Планируемые улучшения
|
||||
|
||||
1. **Массовые операции**
|
||||
- Блокировка нескольких пользователей одновременно
|
||||
- Экспорт списка в CSV/JSON
|
||||
|
||||
2. **Расширенные фильтры**
|
||||
- Диапазон дат регистрации
|
||||
- Неактивные пользователи (по last_activity)
|
||||
- Пользователи без клубной карты
|
||||
|
||||
3. **История блокировок**
|
||||
- Журнал всех блокировок/разблокировок
|
||||
- Кто и когда заблокировал
|
||||
- Причина блокировки (опциональное поле)
|
||||
|
||||
4. **Автоматическая блокировка**
|
||||
- При определенном количестве жалоб от других пользователей
|
||||
- При обнаружении спам-активности
|
||||
|
||||
5. **Уведомления**
|
||||
- Уведомление пользователя о блокировке (опционально)
|
||||
- Уведомление администраторов о подозрительной активности
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Проблема: Поиск не находит пользователя
|
||||
**Решение:**
|
||||
- Убедиться, что пользователь существует в базе
|
||||
- Проверить правильность написания (регистр не важен)
|
||||
- Попробовать поиск по Telegram ID
|
||||
|
||||
### Проблема: Блокировка не применяется
|
||||
**Решение:**
|
||||
- Проверить логи: `docker logs lottery_bot | grep "блокирован"`
|
||||
- Убедиться, что транзакция закоммитилась
|
||||
- Проверить поле в БД: `SELECT telegram_id, is_chat_banned FROM users WHERE id = ?`
|
||||
|
||||
### Проблема: Медленный поиск (>2 секунд)
|
||||
**Решение:**
|
||||
- Добавить индексы (см. раздел "Производительность")
|
||||
- Проверить EXPLAIN ANALYZE для поискового запроса
|
||||
- Рассмотреть использование полнотекстового поиска PostgreSQL
|
||||
|
||||
## Тестирование
|
||||
|
||||
### Ручное тестирование
|
||||
|
||||
1. **Тест поиска:**
|
||||
```
|
||||
- Поиск по существующему username
|
||||
- Поиск по несуществующему username
|
||||
- Поиск с частичным совпадением
|
||||
- Поиск по Telegram ID
|
||||
```
|
||||
|
||||
2. **Тест блокировки:**
|
||||
```
|
||||
- Заблокировать пользователя
|
||||
- Попытаться отправить сообщение от заблокированного пользователя
|
||||
- Разблокировать пользователя
|
||||
- Убедиться, что сообщения проходят
|
||||
```
|
||||
|
||||
3. **Тест пагинации:**
|
||||
```
|
||||
- Создать >15 пользователей
|
||||
- Проверить навигацию вперед/назад
|
||||
- Проверить корректность подсчета страниц
|
||||
```
|
||||
|
||||
### Автоматическое тестирование
|
||||
|
||||
```python
|
||||
# tests/test_user_management.py
|
||||
import pytest
|
||||
from src.core.user_management import UserManagementService
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_users_by_username(session):
|
||||
users, total = await UserManagementService.search_users(
|
||||
session, query="@testuser"
|
||||
)
|
||||
assert len(users) > 0
|
||||
assert users[0].username == "testuser"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ban_user(session):
|
||||
success = await UserManagementService.ban_user_in_chat(session, 1)
|
||||
assert success == True
|
||||
|
||||
user = await UserManagementService.get_user_by_id(session, 1)
|
||||
assert user.is_chat_banned == True
|
||||
```
|
||||
|
||||
## Заключение
|
||||
|
||||
Система управления пользователями предоставляет:
|
||||
- ✅ Быстрый и удобный поиск среди больших баз данных
|
||||
- ✅ Простое управление блокировками в чате
|
||||
- ✅ Масштабируемость для 1000+ пользователей
|
||||
- ✅ Интуитивный интерфейс для администраторов
|
||||
- ✅ Интеграцию с системой разрешений чата
|
||||
|
||||
Система готова к использованию и может быть расширена дополнительными функциями по мере необходимости.
|
||||
Reference in New Issue
Block a user