Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c29609e4a | ||
|
|
cb0f94c718 | ||
| b2c27040aa | |||
|
|
47c5b2f083 | ||
|
|
e0e0a6de51 | ||
| 502c07a2c9 |
@@ -1,37 +0,0 @@
|
|||||||
__pycache__/
|
|
||||||
*.py[cod]
|
|
||||||
*.pyo
|
|
||||||
*.pyd
|
|
||||||
*.so
|
|
||||||
*.egg-info/
|
|
||||||
.eggs/
|
|
||||||
.env
|
|
||||||
.venv
|
|
||||||
.vscode/
|
|
||||||
.idea/
|
|
||||||
.git/
|
|
||||||
.gitignore
|
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
|
||||||
**/__pycache__/
|
|
||||||
**/*.pyc
|
|
||||||
**/*.pyo
|
|
||||||
**/*.pyd
|
|
||||||
|
|
||||||
# Local settings
|
|
||||||
settings_example.ini
|
|
||||||
|
|
||||||
# Databases and runtime files
|
|
||||||
*.db
|
|
||||||
*.db-shm
|
|
||||||
*.db-wal
|
|
||||||
logs/
|
|
||||||
|
|
||||||
# Tests and artifacts
|
|
||||||
.coverage
|
|
||||||
.pytest_cache/
|
|
||||||
htmlcov/
|
|
||||||
**/tests/
|
|
||||||
|
|
||||||
# Stickers and large assets (if not needed at runtime)
|
|
||||||
Stick/
|
|
||||||
42
.gitignore
vendored
42
.gitignore
vendored
@@ -1,44 +1,4 @@
|
|||||||
/database/tg-bot-database.db
|
/database/tg-bot-database
|
||||||
/database/tg-bot-database.db-shm
|
|
||||||
/database/tg-bot-database.db-wal
|
|
||||||
/database/test.db
|
|
||||||
/database/test.db-shm
|
|
||||||
/database/test.db-wal
|
|
||||||
/settings.ini
|
/settings.ini
|
||||||
/myenv/
|
/myenv/
|
||||||
/venv/
|
/venv/
|
||||||
/.idea/
|
|
||||||
/logs/*.log
|
|
||||||
|
|
||||||
# Testing and coverage files
|
|
||||||
.coverage
|
|
||||||
coverage.xml
|
|
||||||
htmlcov/
|
|
||||||
.pytest_cache/
|
|
||||||
__pycache__/
|
|
||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
*.pyd
|
|
||||||
.Python
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Test database files
|
|
||||||
database/test.db
|
|
||||||
test.db
|
|
||||||
*.db
|
|
||||||
|
|
||||||
# IDE and editor files
|
|
||||||
.vscode/
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
*~
|
|
||||||
|
|
||||||
# OS generated files
|
|
||||||
.DS_Store
|
|
||||||
.DS_Store?
|
|
||||||
._*
|
|
||||||
.Spotlight-V100
|
|
||||||
.Trashes
|
|
||||||
ehthumbs.db
|
|
||||||
Thumbs.db
|
|
||||||
PERFORMANCE_IMPROVEMENTS.md
|
|
||||||
|
|||||||
37
Dockerfile
37
Dockerfile
@@ -1,37 +0,0 @@
|
|||||||
# syntax=docker/dockerfile:1
|
|
||||||
|
|
||||||
# Use a lightweight Python image
|
|
||||||
FROM python:3.11-slim
|
|
||||||
|
|
||||||
# Prevent Python from writing .pyc files and enable unbuffered logs
|
|
||||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
|
||||||
PYTHONUNBUFFERED=1
|
|
||||||
|
|
||||||
# Install system dependencies (if required by Python packages)
|
|
||||||
RUN apt-get update \
|
|
||||||
&& apt-get install -y --no-install-recommends build-essential \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Set working directory
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Create non-root user
|
|
||||||
RUN useradd -m appuser \
|
|
||||||
&& chown -R appuser:appuser /app
|
|
||||||
|
|
||||||
# Install Python dependencies first for better layer caching
|
|
||||||
COPY requirements.txt ./
|
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
|
||||||
|
|
||||||
# Copy project files
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Ensure runtime directories exist and are writable
|
|
||||||
RUN mkdir -p logs database \
|
|
||||||
&& chown -R appuser:appuser /app
|
|
||||||
|
|
||||||
# Switch to non-root user
|
|
||||||
USER appuser
|
|
||||||
|
|
||||||
# Run the bot
|
|
||||||
CMD ["python", "run_helper.py"]
|
|
||||||
73
Makefile
73
Makefile
@@ -1,73 +0,0 @@
|
|||||||
.PHONY: help test test-db test-coverage test-html clean install
|
|
||||||
|
|
||||||
# Default target
|
|
||||||
help:
|
|
||||||
@echo "Available commands:"
|
|
||||||
@echo " install - Install dependencies"
|
|
||||||
@echo " test - Run all tests"
|
|
||||||
@echo " test-db - Run database tests only"
|
|
||||||
@echo " test-bot - Run bot startup and handler tests only"
|
|
||||||
@echo " test-media - Run media handler tests only"
|
|
||||||
@echo " test-errors - Run error handling tests only"
|
|
||||||
@echo " test-utils - Run utility functions tests only"
|
|
||||||
@echo " test-keyboards - Run keyboard and filter tests only"
|
|
||||||
@echo " test-coverage - Run tests with coverage report (helper_bot + database)"
|
|
||||||
@echo " test-html - Run tests and generate HTML coverage report"
|
|
||||||
@echo " clean - Clean up generated files"
|
|
||||||
@echo " coverage - Show coverage report only"
|
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
install:
|
|
||||||
python3 -m pip install -r requirements.txt
|
|
||||||
python3 -m pip install pytest-cov
|
|
||||||
|
|
||||||
# Run all tests
|
|
||||||
test:
|
|
||||||
python3 -m pytest tests/ -v
|
|
||||||
|
|
||||||
# Run database tests only
|
|
||||||
test-db:
|
|
||||||
python3 -m pytest tests/test_db.py -v
|
|
||||||
|
|
||||||
# Run bot tests only
|
|
||||||
test-bot:
|
|
||||||
python3 -m pytest tests/test_bot.py -v
|
|
||||||
|
|
||||||
# Run media handler tests only
|
|
||||||
test-media:
|
|
||||||
python3 -m pytest tests/test_media_handlers.py -v
|
|
||||||
|
|
||||||
# Run error handling tests only
|
|
||||||
test-errors:
|
|
||||||
python3 -m pytest tests/test_error_handling.py -v
|
|
||||||
|
|
||||||
# Run utils tests only
|
|
||||||
test-utils:
|
|
||||||
python3 -m pytest tests/test_utils.py -v
|
|
||||||
|
|
||||||
# Run keyboard and filter tests only
|
|
||||||
test-keyboards:
|
|
||||||
python3 -m pytest tests/test_keyboards_and_filters.py -v
|
|
||||||
|
|
||||||
# Run tests with coverage
|
|
||||||
test-coverage:
|
|
||||||
python3 -m pytest tests/ --cov=helper_bot --cov=database --cov-report=term
|
|
||||||
|
|
||||||
# Run tests and generate HTML coverage report
|
|
||||||
test-html:
|
|
||||||
python3 -m pytest tests/ --cov=helper_bot --cov=database --cov-report=html:htmlcov --cov-report=term
|
|
||||||
@echo "HTML coverage report generated in htmlcov/index.html"
|
|
||||||
|
|
||||||
# Show coverage report only
|
|
||||||
coverage:
|
|
||||||
python3 -m coverage report --include="helper_bot/*,database/*"
|
|
||||||
|
|
||||||
# Clean up generated files
|
|
||||||
clean:
|
|
||||||
rm -rf htmlcov/
|
|
||||||
rm -f coverage.xml
|
|
||||||
rm -f .coverage
|
|
||||||
rm -f database/test.db
|
|
||||||
rm -f test.db
|
|
||||||
find . -type d -name "__pycache__" -exec rm -rf {} +
|
|
||||||
find . -type f -name "*.pyc" -delete
|
|
||||||
@@ -1,259 +0,0 @@
|
|||||||
# Тестирование Telegram Helper Bot
|
|
||||||
|
|
||||||
Этот документ описывает систему тестирования для Telegram Helper Bot.
|
|
||||||
|
|
||||||
## Структура тестов
|
|
||||||
|
|
||||||
Тесты организованы в следующие файлы:
|
|
||||||
|
|
||||||
- `tests/test_bot.py` - Основные тесты бота (запуск, хэндлеры, интеграция)
|
|
||||||
- `tests/test_media_handlers.py` - Тесты обработки медиа-контента
|
|
||||||
- `tests/test_error_handling.py` - Тесты обработки ошибок и граничных случаев
|
|
||||||
- `tests/test_utils.py` - Тесты утилит и вспомогательных функций
|
|
||||||
- `tests/test_keyboards_and_filters.py` - Тесты клавиатур и фильтров
|
|
||||||
- `tests/test_db.py` - Тесты базы данных
|
|
||||||
- `tests/conftest.py` - Общие фикстуры и конфигурация
|
|
||||||
|
|
||||||
## Установка зависимостей
|
|
||||||
|
|
||||||
```bash
|
|
||||||
make install
|
|
||||||
```
|
|
||||||
|
|
||||||
## Запуск тестов
|
|
||||||
|
|
||||||
### Все тесты
|
|
||||||
```bash
|
|
||||||
make test
|
|
||||||
```
|
|
||||||
|
|
||||||
### Отдельные категории тестов
|
|
||||||
```bash
|
|
||||||
# Тесты базы данных
|
|
||||||
make test-db
|
|
||||||
|
|
||||||
# Тесты бота (запуск и хэндлеры)
|
|
||||||
make test-bot
|
|
||||||
|
|
||||||
# Тесты обработки медиа
|
|
||||||
make test-media
|
|
||||||
|
|
||||||
# Тесты обработки ошибок
|
|
||||||
make test-errors
|
|
||||||
|
|
||||||
# Тесты утилит
|
|
||||||
make test-utils
|
|
||||||
|
|
||||||
# Тесты клавиатур и фильтров
|
|
||||||
make test-keyboards
|
|
||||||
```
|
|
||||||
|
|
||||||
### Тесты с покрытием
|
|
||||||
```bash
|
|
||||||
# Покрытие с выводом в терминал
|
|
||||||
make test-coverage
|
|
||||||
|
|
||||||
# Покрытие с HTML отчетом
|
|
||||||
make test-html
|
|
||||||
```
|
|
||||||
|
|
||||||
### Фильтрация тестов
|
|
||||||
```bash
|
|
||||||
# Только unit тесты
|
|
||||||
pytest -m unit
|
|
||||||
|
|
||||||
# Только интеграционные тесты
|
|
||||||
pytest -m integration
|
|
||||||
|
|
||||||
# Только асинхронные тесты
|
|
||||||
pytest -m asyncio
|
|
||||||
|
|
||||||
# Исключить медленные тесты
|
|
||||||
pytest -m "not slow"
|
|
||||||
|
|
||||||
# Конкретный файл тестов
|
|
||||||
pytest tests/test_bot.py
|
|
||||||
|
|
||||||
# Конкретный тест
|
|
||||||
pytest tests/test_bot.py::TestBotStartup::test_bot_initialization
|
|
||||||
```
|
|
||||||
|
|
||||||
## Типы тестов
|
|
||||||
|
|
||||||
### Unit тесты
|
|
||||||
Тестируют отдельные функции и компоненты в изоляции:
|
|
||||||
- Вспомогательные функции (`get_first_name`, `get_text_message`)
|
|
||||||
- Утилиты (`BaseDependencyFactory`, `get_message`)
|
|
||||||
- Фильтры (`ChatTypeFilter`)
|
|
||||||
- Клавиатуры
|
|
||||||
|
|
||||||
### Интеграционные тесты
|
|
||||||
Тестируют взаимодействие между компонентами:
|
|
||||||
- Регистрация роутеров в диспетчере
|
|
||||||
- Обработка сообщений через хэндлеры
|
|
||||||
- Интеграция с базой данных
|
|
||||||
|
|
||||||
### Асинхронные тесты
|
|
||||||
Тестируют асинхронные функции:
|
|
||||||
- Хэндлеры сообщений
|
|
||||||
- Запуск бота
|
|
||||||
- Обработка медиа-контента
|
|
||||||
|
|
||||||
## Моки и фикстуры
|
|
||||||
|
|
||||||
### Основные фикстуры
|
|
||||||
- `mock_message` - Мок сообщения Telegram
|
|
||||||
- `mock_state` - Мок состояния FSM
|
|
||||||
- `mock_db` - Мок базы данных
|
|
||||||
- `mock_bot` - Мок бота
|
|
||||||
- `mock_dispatcher` - Мок диспетчера
|
|
||||||
- `mock_factory` - Мок фабрики зависимостей
|
|
||||||
|
|
||||||
### Специализированные фикстуры
|
|
||||||
- `sample_photo_message` - Сообщение с фото
|
|
||||||
- `sample_video_message` - Сообщение с видео
|
|
||||||
- `sample_audio_message` - Сообщение с аудио
|
|
||||||
- `sample_voice_message` - Голосовое сообщение
|
|
||||||
- `sample_video_note_message` - Видеокружок
|
|
||||||
- `sample_media_group` - Медиагруппа
|
|
||||||
- `sample_text_message` - Текстовое сообщение
|
|
||||||
|
|
||||||
## Покрытие тестами
|
|
||||||
|
|
||||||
### Основные компоненты
|
|
||||||
- ✅ Запуск бота (`start_bot`)
|
|
||||||
- ✅ Приватные хэндлеры (`handle_start_message`, `suggest_post`, etc.)
|
|
||||||
- ✅ Обработка медиа-контента (фото, видео, аудио, голос)
|
|
||||||
- ✅ Обработка ошибок и исключений
|
|
||||||
- ✅ Утилиты и вспомогательные функции
|
|
||||||
- ✅ Клавиатуры и фильтры
|
|
||||||
- ✅ Фабрика зависимостей
|
|
||||||
|
|
||||||
### Тестируемые сценарии
|
|
||||||
- ✅ Новые пользователи
|
|
||||||
- ✅ Существующие пользователи
|
|
||||||
- ✅ Пользователи без username
|
|
||||||
- ✅ Обработка различных типов контента
|
|
||||||
- ✅ Медиагруппы
|
|
||||||
- ✅ Ошибки при получении стикеров
|
|
||||||
- ✅ Ошибки базы данных
|
|
||||||
- ✅ Граничные случаи (пустой текст, отсутствие подписей)
|
|
||||||
|
|
||||||
## Настройка окружения
|
|
||||||
|
|
||||||
### Переменные окружения
|
|
||||||
Для тестов не требуются реальные токены бота или подключения к базе данных, так как все внешние зависимости замоканы.
|
|
||||||
|
|
||||||
### Конфигурация pytest
|
|
||||||
Настройки pytest находятся в файле `pytest.ini`:
|
|
||||||
- Автоматический режим asyncio
|
|
||||||
- Фильтрация предупреждений
|
|
||||||
- Маркеры для категоризации тестов
|
|
||||||
|
|
||||||
## Добавление новых тестов
|
|
||||||
|
|
||||||
### Структура теста
|
|
||||||
```python
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_function_name(mock_message, mock_state, mock_db):
|
|
||||||
"""Описание теста"""
|
|
||||||
# Arrange (подготовка)
|
|
||||||
mock_message.text = "test"
|
|
||||||
|
|
||||||
# Act (действие)
|
|
||||||
result = await function_to_test(mock_message, mock_state)
|
|
||||||
|
|
||||||
# Assert (проверка)
|
|
||||||
assert result is True
|
|
||||||
mock_message.answer.assert_called_once()
|
|
||||||
```
|
|
||||||
|
|
||||||
### Маркировка тестов
|
|
||||||
```python
|
|
||||||
@pytest.mark.unit # Unit тест
|
|
||||||
@pytest.mark.integration # Интеграционный тест
|
|
||||||
@pytest.mark.asyncio # Асинхронный тест
|
|
||||||
@pytest.mark.slow # Медленный тест
|
|
||||||
```
|
|
||||||
|
|
||||||
### Использование фикстур
|
|
||||||
```python
|
|
||||||
def test_with_fixtures(mock_message, sample_photo_message, mock_db):
|
|
||||||
# Используем готовые фикстуры
|
|
||||||
pass
|
|
||||||
```
|
|
||||||
|
|
||||||
## Отладка тестов
|
|
||||||
|
|
||||||
### Подробный вывод
|
|
||||||
```bash
|
|
||||||
pytest -v -s
|
|
||||||
```
|
|
||||||
|
|
||||||
### Остановка на первой ошибке
|
|
||||||
```bash
|
|
||||||
pytest -x
|
|
||||||
```
|
|
||||||
|
|
||||||
### Вывод полного traceback
|
|
||||||
```bash
|
|
||||||
pytest --tb=long
|
|
||||||
```
|
|
||||||
|
|
||||||
### Запуск конкретного теста
|
|
||||||
```bash
|
|
||||||
pytest tests/test_bot.py::TestPrivateHandlers::test_handle_start_message_new_user -v
|
|
||||||
```
|
|
||||||
|
|
||||||
## CI/CD интеграция
|
|
||||||
|
|
||||||
Тесты могут быть интегрированы в CI/CD pipeline:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Пример для GitHub Actions
|
|
||||||
- name: Run tests
|
|
||||||
run: |
|
|
||||||
make install
|
|
||||||
make test-coverage
|
|
||||||
```
|
|
||||||
|
|
||||||
## Покрытие кода
|
|
||||||
|
|
||||||
Для просмотра покрытия кода:
|
|
||||||
```bash
|
|
||||||
make test-html
|
|
||||||
# Открыть htmlcov/index.html в браузере
|
|
||||||
```
|
|
||||||
|
|
||||||
## Лучшие практики
|
|
||||||
|
|
||||||
1. **Изоляция тестов** - каждый тест должен быть независимым
|
|
||||||
2. **Использование моков** - избегайте реальных внешних зависимостей
|
|
||||||
3. **Описательные имена** - имена тестов должны описывать что тестируется
|
|
||||||
4. **Arrange-Act-Assert** - структурируйте тесты по этому паттерну
|
|
||||||
5. **Фикстуры** - используйте фикстуры для переиспользования кода
|
|
||||||
6. **Маркировка** - правильно маркируйте тесты для фильтрации
|
|
||||||
|
|
||||||
## Устранение неполадок
|
|
||||||
|
|
||||||
### Ошибки импорта
|
|
||||||
Убедитесь, что Python path настроен правильно:
|
|
||||||
```bash
|
|
||||||
export PYTHONPATH="${PYTHONPATH}:$(pwd)"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Ошибки asyncio
|
|
||||||
Для асинхронных тестов используйте маркер `@pytest.mark.asyncio`
|
|
||||||
|
|
||||||
### Ошибки моков
|
|
||||||
Проверьте, что все внешние зависимости замоканы:
|
|
||||||
```python
|
|
||||||
with patch('module.function') as mock_func:
|
|
||||||
# тест
|
|
||||||
```
|
|
||||||
|
|
||||||
### Медленные тесты
|
|
||||||
Используйте маркер `@pytest.mark.slow` для медленных тестов и исключайте их при необходимости:
|
|
||||||
```bash
|
|
||||||
pytest -m "not slow"
|
|
||||||
```
|
|
||||||
2555
database/db.py
2555
database/db.py
File diff suppressed because it is too large
Load Diff
BIN
helper_bot/handlers/admin/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
helper_bot/handlers/admin/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
@@ -1,321 +1,179 @@
|
|||||||
import traceback
|
import traceback
|
||||||
import html
|
|
||||||
|
from aiogram import Router, types, F
|
||||||
from aiogram import Router, types, F
|
from aiogram.filters import Command, StateFilter
|
||||||
from aiogram.filters import Command, StateFilter
|
from aiogram.fsm.context import FSMContext
|
||||||
from aiogram.fsm.context import FSMContext
|
|
||||||
|
from helper_bot.filters.main import ChatTypeFilter
|
||||||
from helper_bot.filters.main import ChatTypeFilter
|
from helper_bot.keyboards.keyboards import get_reply_keyboard_admin, create_keyboard_with_pagination, \
|
||||||
from helper_bot.keyboards.keyboards import get_reply_keyboard_admin, create_keyboard_with_pagination, \
|
create_keyboard_for_ban_days, create_keyboard_for_approve_ban, create_keyboard_for_ban_reason
|
||||||
create_keyboard_for_ban_days, create_keyboard_for_approve_ban, create_keyboard_for_ban_reason
|
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory
|
||||||
from helper_bot.utils.base_dependency_factory import get_global_instance
|
from helper_bot.utils.helper_func import check_access, add_days_to_date, get_banned_users_buttons, get_banned_users_list
|
||||||
from helper_bot.utils.helper_func import check_access, add_days_to_date, get_banned_users_buttons, get_banned_users_list
|
from logs.custom_logger import logger
|
||||||
from logs.custom_logger import logger
|
|
||||||
|
admin_router = Router()
|
||||||
admin_router = Router()
|
|
||||||
|
bdf = BaseDependencyFactory()
|
||||||
bdf = get_global_instance()
|
GROUP_FOR_POST = bdf.settings['Telegram']['group_for_posts']
|
||||||
GROUP_FOR_POST = bdf.settings['Telegram']['group_for_posts']
|
GROUP_FOR_MESSAGE = bdf.settings['Telegram']['group_for_message']
|
||||||
GROUP_FOR_MESSAGE = bdf.settings['Telegram']['group_for_message']
|
MAIN_PUBLIC = bdf.settings['Telegram']['main_public']
|
||||||
MAIN_PUBLIC = bdf.settings['Telegram']['main_public']
|
GROUP_FOR_LOGS = bdf.settings['Telegram']['group_for_logs']
|
||||||
GROUP_FOR_LOGS = bdf.settings['Telegram']['group_for_logs']
|
IMPORTANT_LOGS = bdf.settings['Telegram']['important_logs']
|
||||||
IMPORTANT_LOGS = bdf.settings['Telegram']['important_logs']
|
PREVIEW_LINK = bdf.settings['Telegram']['preview_link']
|
||||||
PREVIEW_LINK = bdf.settings['Telegram']['preview_link']
|
LOGS = bdf.settings['Settings']['logs']
|
||||||
LOGS = bdf.settings['Settings']['logs']
|
TEST = bdf.settings['Settings']['test']
|
||||||
TEST = bdf.settings['Settings']['test']
|
|
||||||
|
BotDB = bdf.get_db()
|
||||||
BotDB = bdf.get_db()
|
|
||||||
|
|
||||||
|
@admin_router.message(
|
||||||
@admin_router.message(
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
Command('admin')
|
||||||
Command('admin')
|
)
|
||||||
)
|
async def admin_panel(message: types.Message, state: FSMContext):
|
||||||
async def admin_panel(message: types.Message, state: FSMContext):
|
try:
|
||||||
try:
|
if check_access(message.from_user.id):
|
||||||
if check_access(message.from_user.id, BotDB):
|
await state.set_state("ADMIN")
|
||||||
await state.set_state("ADMIN")
|
logger.info(f"Запуск админ панели для пользователя: {message.from_user.id}")
|
||||||
logger.info(f"Запуск админ панели для пользователя: {message.from_user.id}")
|
markup = get_reply_keyboard_admin()
|
||||||
markup = get_reply_keyboard_admin()
|
await message.answer("Добро пожаловать в админку. Выбери что хочешь:",
|
||||||
await message.answer("Добро пожаловать в админку. Выбери что хочешь:",
|
reply_markup=markup)
|
||||||
reply_markup=markup)
|
else:
|
||||||
else:
|
await message.answer('Доступ запрещен, досвидания!')
|
||||||
await message.answer('Доступ запрещен, досвидания!')
|
except Exception as e:
|
||||||
except Exception as e:
|
logger.error(f"Ошибка при запуске админ панели: {e}")
|
||||||
logger.error(f"Ошибка при запуске админ панели: {e}")
|
await message.bot.send_message(IMPORTANT_LOGS,
|
||||||
await message.bot.send_message(IMPORTANT_LOGS,
|
f'Ошибка в функции admin_panel {e}. Traceback: {traceback.format_exc()}')
|
||||||
f'Ошибка в функции admin_panel {e}. Traceback: {traceback.format_exc()}')
|
|
||||||
|
|
||||||
|
@admin_router.message(
|
||||||
@admin_router.message(
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
StateFilter("ADMIN"),
|
||||||
StateFilter("ADMIN"),
|
F.text == 'Бан (Список)'
|
||||||
F.text == 'Бан (Список)'
|
)
|
||||||
)
|
async def get_last_users(message: types.Message):
|
||||||
async def get_last_users(message: types.Message):
|
logger.info(
|
||||||
logger.info(
|
f"Попытка получения списка последних пользователей. Текст сообщения: {message.text} Имя автора сообщения: {message.from_user.full_name})")
|
||||||
f"Попытка получения списка последних пользователей. Текст сообщения: {message.text} Имя автора сообщения: {message.from_user.full_name})")
|
list_users = BotDB.get_last_users_from_db()
|
||||||
list_users = BotDB.get_last_users_from_db()
|
keyboard = create_keyboard_with_pagination(1, len(list_users), list_users, 'ban')
|
||||||
keyboard = create_keyboard_with_pagination(1, len(list_users), list_users, 'ban')
|
await message.answer(text="Список пользователей которые последними обращались к боту",
|
||||||
await message.answer(text="Список пользователей которые последними обращались к боту",
|
reply_markup=keyboard)
|
||||||
reply_markup=keyboard)
|
|
||||||
|
|
||||||
|
@admin_router.message(
|
||||||
@admin_router.message(
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
StateFilter("ADMIN"),
|
||||||
StateFilter("ADMIN"),
|
F.text == 'Бан по нику'
|
||||||
F.text == 'Бан по нику'
|
)
|
||||||
)
|
async def ban_by_nickname(message: types.Message, state: FSMContext):
|
||||||
async def ban_by_nickname(message: types.Message, state: FSMContext):
|
await message.answer('Пришли мне username блокируемого пользователя')
|
||||||
await message.answer('Пришли мне username блокируемого пользователя')
|
await state.set_state('PRE_BAN')
|
||||||
await state.set_state('PRE_BAN')
|
|
||||||
|
|
||||||
|
@admin_router.message(
|
||||||
@admin_router.message(
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
F.text == 'Отменить'
|
||||||
StateFilter("ADMIN"),
|
)
|
||||||
F.text == 'Бан по ID'
|
async def decline_ban(message: types.Message, state: FSMContext):
|
||||||
)
|
await state.set_data({})
|
||||||
async def ban_by_id(message: types.Message, state: FSMContext):
|
await state.set_state("ADMIN")
|
||||||
await message.answer('Пришли мне ID блокируемого пользователя')
|
logger.info(f"Отмена процедуры блокировки")
|
||||||
await state.set_state('PRE_BAN_ID')
|
markup = get_reply_keyboard_admin()
|
||||||
|
await message.answer('Вернулись в меню', reply_markup=markup)
|
||||||
|
|
||||||
@admin_router.message(
|
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
@admin_router.message(
|
||||||
StateFilter("ADMIN"),
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
F.text == 'Тестовый бан'
|
StateFilter("PRE_BAN")
|
||||||
)
|
)
|
||||||
async def ban_by_forward(message: types.Message, state: FSMContext):
|
async def ban_by_nickname_step_2(message: types.Message, state: FSMContext):
|
||||||
await message.answer('Перешлите мне сообщение от пользователя, которого хотите заблокировать')
|
logger.info(
|
||||||
await state.set_state('PRE_BAN_FORWARD')
|
f"Функция ban_by_nickname_2. Получен никнейм пользователя: {message.text}")
|
||||||
|
user_name = message.text
|
||||||
|
user_id = BotDB.get_user_id_by_username(user_name)
|
||||||
@admin_router.message(
|
await state.update_data(user_id=user_id, user_name=user_name, message_for_user=None,
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
date_to_unban=None)
|
||||||
F.text == 'Отменить'
|
full_name = BotDB.get_full_name_by_id(user_id)
|
||||||
)
|
markup = create_keyboard_for_ban_reason()
|
||||||
async def decline_ban(message: types.Message, state: FSMContext):
|
await message.answer(
|
||||||
current_state = await state.get_state()
|
text=f"<b>Выбран пользователь:\nid:</b> {user_id}\n<b>username:</b> {user_name}\n"
|
||||||
await state.set_data({})
|
f"Имя:{full_name}\nВыбери причину бана из списка или напиши ее в чат",
|
||||||
await state.set_state("ADMIN")
|
reply_markup=markup)
|
||||||
logger.info(f"Отмена процедуры блокировки из состояния: {current_state}")
|
await state.set_state('BAN_2')
|
||||||
markup = get_reply_keyboard_admin()
|
|
||||||
await message.answer('Вернулись в меню', reply_markup=markup)
|
|
||||||
|
@admin_router.message(
|
||||||
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
@admin_router.message(
|
StateFilter("ADMIN"),
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
F.text == 'Разбан (список)'
|
||||||
StateFilter("PRE_BAN")
|
)
|
||||||
)
|
async def get_banned_users(message):
|
||||||
async def ban_by_nickname_step_2(message: types.Message, state: FSMContext):
|
logger.info(
|
||||||
logger.info(
|
f"Попытка получения списка заблокированных пользователей. Текст сообщения: {message.text} Имя автора сообщения: {message.from_user.full_name})")
|
||||||
f"Функция ban_by_nickname_2. Получен никнейм пользователя: {message.text}")
|
message_text = get_banned_users_list(0)
|
||||||
user_name = message.text
|
buttons_list = get_banned_users_buttons()
|
||||||
user_id = BotDB.get_user_id_by_username(user_name)
|
if buttons_list:
|
||||||
await state.update_data(user_id=user_id, user_name=user_name, message_for_user=None,
|
k = create_keyboard_with_pagination(1, len(buttons_list), buttons_list, 'unlock')
|
||||||
date_to_unban=None)
|
await message.answer(text=message_text, reply_markup=k)
|
||||||
full_name = BotDB.get_full_name_by_id(user_id)
|
else:
|
||||||
markup = create_keyboard_for_ban_reason()
|
await message.answer(text="В списке забанненых пользователей никого нет")
|
||||||
# Экранируем потенциально проблемные символы
|
|
||||||
user_name_escaped = html.escape(str(user_name))
|
|
||||||
full_name_escaped = html.escape(str(full_name))
|
@admin_router.message(
|
||||||
await message.answer(
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
text=f"<b>Выбран пользователь:\nid:</b> {user_id}\n<b>username:</b> {user_name_escaped}\n"
|
StateFilter("BAN_2")
|
||||||
f"Имя:{full_name_escaped}\nВыбери причину бана из списка или напиши ее в чат",
|
)
|
||||||
reply_markup=markup)
|
async def ban_user_step_2(message: types.Message, state: FSMContext):
|
||||||
await state.set_state('BAN_2')
|
user_data = await state.get_data()
|
||||||
|
logger.info(f"Переход на шаг 2 бана пользователя. Словарь с данными для бана: {user_data})")
|
||||||
|
await state.update_data(message_for_user=message.text)
|
||||||
@admin_router.message(
|
markup = create_keyboard_for_ban_days()
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
await message.answer(f"Выбрана причина: {message.text}. Выбери срок бана в днях или напиши "
|
||||||
StateFilter("PRE_BAN_ID")
|
f"его в чат", reply_markup=markup)
|
||||||
)
|
await state.set_state("BAN_3")
|
||||||
async def ban_by_id_step_2(message: types.Message, state: FSMContext):
|
|
||||||
try:
|
|
||||||
user_id = int(message.text)
|
@admin_router.message(
|
||||||
logger.info(f"Функция ban_by_id_step_2. Получен ID пользователя: {user_id}")
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
|
StateFilter("BAN_3")
|
||||||
# Проверяем, существует ли пользователь в базе
|
)
|
||||||
user_info = BotDB.get_user_info_by_id(user_id)
|
async def ban_user_step_3(message: types.Message, state: FSMContext):
|
||||||
if not user_info:
|
logger.info(f"ban_user_step_3. Расчет даты разбана. Входные данные {message.text}")
|
||||||
await message.answer(f"Пользователь с ID {user_id} не найден в базе данных.")
|
if message.text != 'Навсегда':
|
||||||
await state.set_state('ADMIN')
|
count_days = int(message.text)
|
||||||
markup = get_reply_keyboard_admin()
|
date_to_unban = add_days_to_date(count_days)
|
||||||
await message.answer('Вернулись в меню', reply_markup=markup)
|
else:
|
||||||
return
|
date_to_unban = None
|
||||||
|
logger.info(f"ban_user_step_3. Расчет даты разбана. date_to_unban: {date_to_unban}")
|
||||||
user_name = user_info.get('username', 'Неизвестно')
|
await state.update_data(date_to_unban=date_to_unban)
|
||||||
full_name = user_info.get('full_name', 'Неизвестно')
|
user_data = await state.get_data()
|
||||||
|
markup = create_keyboard_for_approve_ban()
|
||||||
await state.update_data(user_id=user_id, user_name=user_name, message_for_user=None,
|
await message.answer(
|
||||||
date_to_unban=None)
|
f"Необходимо подтверждение:\nПользователь:{user_data['user_id']}\nПричина бана:{user_data['message_for_user']}\nСрок бана:{user_data['date_to_unban']}",
|
||||||
|
reply_markup=markup)
|
||||||
markup = create_keyboard_for_ban_reason()
|
await state.set_state("BAN_FINAL")
|
||||||
# Экранируем потенциально проблемные символы
|
|
||||||
user_name_escaped = html.escape(str(user_name))
|
|
||||||
full_name_escaped = html.escape(str(full_name))
|
@admin_router.message(
|
||||||
await message.answer(
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
text=f"<b>Выбран пользователь:\nid:</b> {user_id}\n<b>username:</b> {user_name_escaped}\n"
|
StateFilter("BAN_FINAL"),
|
||||||
f"Имя:{full_name_escaped}\nВыбери причину бана из списка или напиши ее в чат",
|
F.text == 'Подтвердить'
|
||||||
reply_markup=markup)
|
)
|
||||||
await state.set_state('BAN_2')
|
async def approve_ban(message: types.Message, state: FSMContext):
|
||||||
|
user_data = await state.get_data()
|
||||||
except ValueError:
|
logger.info(f"Переход на финальный шаг бана пользователя. Словарь с данными для бана: {user_data})")
|
||||||
await message.answer("Пожалуйста, введите корректный числовой ID пользователя.")
|
exists = BotDB.check_user_in_blacklist(user_data['user_id'])
|
||||||
await state.set_state('ADMIN')
|
if exists:
|
||||||
markup = get_reply_keyboard_admin()
|
await message.reply(f"Пользователь уже был заблокирован ранее.")
|
||||||
await message.answer('Вернулись в меню', reply_markup=markup)
|
logger.info(f"Пользователь: {user_data['user_id']} был заблокирован ранее)")
|
||||||
|
await state.set_state('ADMIN')
|
||||||
|
else:
|
||||||
@admin_router.message(
|
BotDB.set_user_blacklist(user_data['user_id'],
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
user_data['user_name'],
|
||||||
StateFilter("PRE_BAN_FORWARD"),
|
user_data['message_for_user'],
|
||||||
F.forward_from
|
user_data['date_to_unban'])
|
||||||
)
|
await message.reply(f"Пользователь {user_data['user_name']} успешно заблокирован.")
|
||||||
async def ban_by_forward_step_2(message: types.Message, state: FSMContext):
|
logger.info(f"Пользователь: {user_data['user_id']} успешно заблокирован)")
|
||||||
"""Обработчик пересланных сообщений для бана пользователя"""
|
await state.set_state('ADMIN')
|
||||||
try:
|
markup = get_reply_keyboard_admin()
|
||||||
# Получаем информацию о пользователе из пересланного сообщения
|
await message.answer('Вернулись в меню', reply_markup=markup)
|
||||||
forwarded_user = message.forward_from
|
|
||||||
|
|
||||||
if not forwarded_user:
|
|
||||||
await message.answer("Не удалось получить информацию о пользователе из пересланного сообщения. Возможно, пользователь скрыл возможность пересылки своих сообщений.")
|
|
||||||
await state.set_state('ADMIN')
|
|
||||||
markup = get_reply_keyboard_admin()
|
|
||||||
await message.answer('Вернулись в меню', reply_markup=markup)
|
|
||||||
return
|
|
||||||
|
|
||||||
user_id = forwarded_user.id
|
|
||||||
user_name = forwarded_user.username or "private_username"
|
|
||||||
full_name = forwarded_user.full_name or "Неизвестно"
|
|
||||||
|
|
||||||
logger.info(f"Функция ban_by_forward_step_2. Получен пользователь из пересланного сообщения: ID={user_id}, username={user_name}, full_name={full_name}")
|
|
||||||
|
|
||||||
# Проверяем, существует ли пользователь в базе
|
|
||||||
user_info = BotDB.get_user_info_by_id(user_id)
|
|
||||||
if not user_info:
|
|
||||||
# Если пользователя нет в базе, используем информацию из пересланного сообщения
|
|
||||||
logger.info(f"Пользователь с ID {user_id} не найден в базе данных, используем данные из пересланного сообщения")
|
|
||||||
user_name = user_name
|
|
||||||
full_name = full_name
|
|
||||||
else:
|
|
||||||
# Если пользователь есть в базе, используем данные из базы
|
|
||||||
user_name = user_info.get('username', user_name)
|
|
||||||
full_name = user_info.get('full_name', full_name)
|
|
||||||
|
|
||||||
await state.update_data(user_id=user_id, user_name=user_name, message_for_user=None,
|
|
||||||
date_to_unban=None)
|
|
||||||
|
|
||||||
markup = create_keyboard_for_ban_reason()
|
|
||||||
# Экранируем потенциально проблемные символы
|
|
||||||
user_name_escaped = html.escape(str(user_name))
|
|
||||||
full_name_escaped = html.escape(str(full_name))
|
|
||||||
await message.answer(
|
|
||||||
text=f"<b>Выбран пользователь из пересланного сообщения:\nid:</b> {user_id}\n<b>username:</b> {user_name_escaped}\n"
|
|
||||||
f"Имя:{full_name_escaped}\nВыбери причину бана из списка или напиши ее в чат",
|
|
||||||
reply_markup=markup)
|
|
||||||
await state.set_state('BAN_2')
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Ошибка при обработке пересланного сообщения: {e}")
|
|
||||||
await message.answer("Произошла ошибка при обработке пересланного сообщения.")
|
|
||||||
await state.set_state('ADMIN')
|
|
||||||
markup = get_reply_keyboard_admin()
|
|
||||||
await message.answer('Вернулись в меню', reply_markup=markup)
|
|
||||||
|
|
||||||
|
|
||||||
@admin_router.message(
|
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
|
||||||
StateFilter("PRE_BAN_FORWARD")
|
|
||||||
)
|
|
||||||
async def ban_by_forward_invalid(message: types.Message, state: FSMContext):
|
|
||||||
"""Обработчик для случаев, когда сообщение не является пересланным или не содержит информацию о пользователе"""
|
|
||||||
if message.forward_from_chat:
|
|
||||||
await message.answer("Пересланное сообщение из канала или группы не содержит информацию о конкретном пользователе. Пожалуйста, перешлите сообщение из приватного чата.")
|
|
||||||
else:
|
|
||||||
await message.answer("Пожалуйста, перешлите сообщение от пользователя, которого хотите заблокировать. Обычное сообщение не подходит.")
|
|
||||||
|
|
||||||
|
|
||||||
@admin_router.message(
|
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
|
||||||
StateFilter("ADMIN"),
|
|
||||||
F.text == 'Разбан (список)'
|
|
||||||
)
|
|
||||||
async def get_banned_users(message):
|
|
||||||
logger.info(
|
|
||||||
f"Попытка получения списка заблокированных пользователей. Текст сообщения: {message.text} Имя автора сообщения: {message.from_user.full_name})")
|
|
||||||
message_text = get_banned_users_list(0, BotDB)
|
|
||||||
buttons_list = get_banned_users_buttons(BotDB)
|
|
||||||
if buttons_list:
|
|
||||||
k = create_keyboard_with_pagination(1, len(buttons_list), buttons_list, 'unlock')
|
|
||||||
await message.answer(text=message_text, reply_markup=k)
|
|
||||||
else:
|
|
||||||
await message.answer(text="В списке забанненых пользователей никого нет")
|
|
||||||
|
|
||||||
|
|
||||||
@admin_router.message(
|
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
|
||||||
StateFilter("BAN_2")
|
|
||||||
)
|
|
||||||
async def ban_user_step_2(message: types.Message, state: FSMContext):
|
|
||||||
user_data = await state.get_data()
|
|
||||||
logger.info(f"Переход на шаг 2 бана пользователя. Словарь с данными для бана: {user_data})")
|
|
||||||
await state.update_data(message_for_user=message.text)
|
|
||||||
markup = create_keyboard_for_ban_days()
|
|
||||||
# Экранируем message.text для безопасного использования
|
|
||||||
safe_message_text = html.escape(str(message.text)) if message.text else ""
|
|
||||||
await message.answer(f"Выбрана причина: {safe_message_text}. Выбери срок бана в днях или напиши "
|
|
||||||
f"его в чат", reply_markup=markup)
|
|
||||||
await state.set_state("BAN_3")
|
|
||||||
|
|
||||||
|
|
||||||
@admin_router.message(
|
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
|
||||||
StateFilter("BAN_3")
|
|
||||||
)
|
|
||||||
async def ban_user_step_3(message: types.Message, state: FSMContext):
|
|
||||||
logger.info(f"ban_user_step_3. Расчет даты разбана. Входные данные {message.text}")
|
|
||||||
if message.text != 'Навсегда':
|
|
||||||
count_days = int(message.text)
|
|
||||||
date_to_unban = add_days_to_date(count_days)
|
|
||||||
else:
|
|
||||||
date_to_unban = None
|
|
||||||
logger.info(f"ban_user_step_3. Расчет даты разбана. date_to_unban: {date_to_unban}")
|
|
||||||
await state.update_data(date_to_unban=date_to_unban)
|
|
||||||
user_data = await state.get_data()
|
|
||||||
markup = create_keyboard_for_approve_ban()
|
|
||||||
# Экранируем user_data для безопасного использования
|
|
||||||
safe_message_for_user = html.escape(str(user_data['message_for_user'])) if user_data.get('message_for_user') else ""
|
|
||||||
safe_date_to_unban = html.escape(str(user_data['date_to_unban'])) if user_data.get('date_to_unban') else ""
|
|
||||||
await message.answer(
|
|
||||||
f"Необходимо подтверждение:\nПользователь:{user_data['user_id']}\nПричина бана:{safe_message_for_user}\nСрок бана:{safe_date_to_unban}",
|
|
||||||
reply_markup=markup)
|
|
||||||
await state.set_state("BAN_FINAL")
|
|
||||||
|
|
||||||
|
|
||||||
@admin_router.message(
|
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
|
||||||
StateFilter("BAN_FINAL"),
|
|
||||||
F.text == 'Подтвердить'
|
|
||||||
)
|
|
||||||
async def approve_ban(message: types.Message, state: FSMContext):
|
|
||||||
user_data = await state.get_data()
|
|
||||||
logger.info(f"Переход на финальный шаг бана пользователя. Словарь с данными для бана: {user_data})")
|
|
||||||
exists = BotDB.check_user_in_blacklist(user_data['user_id'])
|
|
||||||
if exists:
|
|
||||||
await message.reply(f"Пользователь уже был заблокирован ранее.")
|
|
||||||
logger.info(f"Пользователь: {user_data['user_id']} был заблокирован ранее)")
|
|
||||||
await state.set_state('ADMIN')
|
|
||||||
else:
|
|
||||||
BotDB.set_user_blacklist(user_data['user_id'],
|
|
||||||
user_data['user_name'],
|
|
||||||
user_data['message_for_user'],
|
|
||||||
user_data['date_to_unban'])
|
|
||||||
# Экранируем user_name для безопасного использования
|
|
||||||
safe_user_name = html.escape(str(user_data['user_name'])) if user_data.get('user_name') else "Неизвестный пользователь"
|
|
||||||
await message.reply(f"Пользователь {safe_user_name} успешно заблокирован.")
|
|
||||||
logger.info(f"Пользователь: {user_data['user_id']} успешно заблокирован)")
|
|
||||||
await state.set_state('ADMIN')
|
|
||||||
markup = get_reply_keyboard_admin()
|
|
||||||
await message.answer('Вернулись в меню', reply_markup=markup)
|
|
||||||
|
|||||||
Binary file not shown.
BIN
helper_bot/handlers/callback/__pycache__/main.cpython-312.pyc
Normal file
BIN
helper_bot/handlers/callback/__pycache__/main.cpython-312.pyc
Normal file
Binary file not shown.
@@ -7,7 +7,7 @@ from aiogram.types import CallbackQuery
|
|||||||
|
|
||||||
from helper_bot.keyboards.keyboards import create_keyboard_with_pagination, get_reply_keyboard_admin, \
|
from helper_bot.keyboards.keyboards import create_keyboard_with_pagination, get_reply_keyboard_admin, \
|
||||||
create_keyboard_for_ban_reason
|
create_keyboard_for_ban_reason
|
||||||
from helper_bot.utils.base_dependency_factory import get_global_instance
|
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory
|
||||||
from helper_bot.utils.helper_func import send_text_message, send_photo_message, get_banned_users_list, \
|
from helper_bot.utils.helper_func import send_text_message, send_photo_message, get_banned_users_list, \
|
||||||
get_banned_users_buttons, delete_user_blacklist, send_media_group_to_channel, \
|
get_banned_users_buttons, delete_user_blacklist, send_media_group_to_channel, \
|
||||||
send_video_message, send_video_note_message, send_audio_message, send_voice_message
|
send_video_message, send_video_note_message, send_audio_message, send_voice_message
|
||||||
@@ -15,7 +15,7 @@ from logs.custom_logger import logger
|
|||||||
|
|
||||||
callback_router = Router()
|
callback_router = Router()
|
||||||
|
|
||||||
bdf = get_global_instance()
|
bdf = BaseDependencyFactory()
|
||||||
GROUP_FOR_POST = bdf.settings['Telegram']['group_for_posts']
|
GROUP_FOR_POST = bdf.settings['Telegram']['group_for_posts']
|
||||||
GROUP_FOR_MESSAGE = bdf.settings['Telegram']['group_for_message']
|
GROUP_FOR_MESSAGE = bdf.settings['Telegram']['group_for_message']
|
||||||
MAIN_PUBLIC = bdf.settings['Telegram']['main_public']
|
MAIN_PUBLIC = bdf.settings['Telegram']['main_public']
|
||||||
@@ -221,11 +221,8 @@ async def process_ban_user(call: CallbackQuery, state: FSMContext):
|
|||||||
await state.update_data(user_id=user_id, user_name=user_name, message_for_user=None,
|
await state.update_data(user_id=user_id, user_name=user_name, message_for_user=None,
|
||||||
date_to_unban=None)
|
date_to_unban=None)
|
||||||
markup = create_keyboard_for_ban_reason()
|
markup = create_keyboard_for_ban_reason()
|
||||||
# Экранируем потенциально проблемные символы
|
|
||||||
user_name_escaped = html.escape(str(user_name))
|
|
||||||
full_name_escaped = html.escape(str(call.message.from_user.full_name))
|
|
||||||
await call.message.answer(
|
await call.message.answer(
|
||||||
text=f"<b>Выбран пользователь:\nid:</b> {user_id}\n<b>username:</b> {user_name_escaped}\nИмя:{full_name_escaped}\nВыбери причину бана из списка или напиши ее в чат",
|
text=f"<b>Выбран пользователь:\nid:</b> {user_id}\n<b>username:</b> {user_name}\nИмя:{call.message.from_user.full_name}\nВыбери причину бана из списка или напиши ее в чат",
|
||||||
reply_markup=markup)
|
reply_markup=markup)
|
||||||
await state.set_state('BAN_2')
|
await state.set_state('BAN_2')
|
||||||
else:
|
else:
|
||||||
@@ -240,7 +237,7 @@ async def process_ban_user(call: CallbackQuery, state: FSMContext):
|
|||||||
async def process_unlock_user(call: CallbackQuery):
|
async def process_unlock_user(call: CallbackQuery):
|
||||||
user_id = call.data[7:]
|
user_id = call.data[7:]
|
||||||
user_name = BotDB.get_username(user_id=user_id)
|
user_name = BotDB.get_username(user_id=user_id)
|
||||||
delete_user_blacklist(user_id, BotDB)
|
delete_user_blacklist(user_id)
|
||||||
logger.info(f"Разблокирован пользователь с ID: {user_id} username:{user_name}")
|
logger.info(f"Разблокирован пользователь с ID: {user_id} username:{user_name}")
|
||||||
username = BotDB.get_username(user_id)
|
username = BotDB.get_username(user_id)
|
||||||
await call.answer(f'Пользователь разблокирован {username}', show_alert=True)
|
await call.answer(f'Пользователь разблокирован {username}', show_alert=True)
|
||||||
@@ -273,12 +270,12 @@ async def change_page(call: CallbackQuery):
|
|||||||
reply_markup=keyboard)
|
reply_markup=keyboard)
|
||||||
else:
|
else:
|
||||||
# Готовим сообщения
|
# Готовим сообщения
|
||||||
message_user = get_banned_users_list(int(page_number) * 7 - 7, BotDB)
|
message_user = get_banned_users_list(int(page_number) * 7 - 7)
|
||||||
await call.bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
|
await call.bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
|
||||||
text=message_user)
|
text=message_user)
|
||||||
|
|
||||||
# Готовим клавиатуру
|
# Готовим клавиатуру
|
||||||
buttons = get_banned_users_buttons(BotDB)
|
buttons = get_banned_users_buttons()
|
||||||
keyboard = create_keyboard_with_pagination(int(call.data[5:]), len(buttons), buttons, 'unlock')
|
keyboard = create_keyboard_with_pagination(int(call.data[5:]), len(buttons), buttons, 'unlock')
|
||||||
await call.bot.edit_message_reply_markup(chat_id=call.message.chat.id, message_id=call.message.message_id,
|
await call.bot.edit_message_reply_markup(chat_id=call.message.chat.id, message_id=call.message.message_id,
|
||||||
reply_markup=keyboard)
|
reply_markup=keyboard)
|
||||||
|
|||||||
BIN
helper_bot/handlers/group/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
helper_bot/handlers/group/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
@@ -3,13 +3,13 @@ from aiogram.fsm.context import FSMContext
|
|||||||
|
|
||||||
from helper_bot.filters.main import ChatTypeFilter
|
from helper_bot.filters.main import ChatTypeFilter
|
||||||
from helper_bot.keyboards.keyboards import get_reply_keyboard_leave_chat
|
from helper_bot.keyboards.keyboards import get_reply_keyboard_leave_chat
|
||||||
from helper_bot.utils.base_dependency_factory import get_global_instance
|
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory
|
||||||
from helper_bot.utils.helper_func import send_text_message
|
from helper_bot.utils.helper_func import send_text_message
|
||||||
from logs.custom_logger import logger
|
from logs.custom_logger import logger
|
||||||
|
|
||||||
group_router = Router()
|
group_router = Router()
|
||||||
|
|
||||||
bdf = get_global_instance()
|
bdf = BaseDependencyFactory()
|
||||||
GROUP_FOR_POST = bdf.settings['Telegram']['group_for_posts']
|
GROUP_FOR_POST = bdf.settings['Telegram']['group_for_posts']
|
||||||
GROUP_FOR_MESSAGE = bdf.settings['Telegram']['group_for_message']
|
GROUP_FOR_MESSAGE = bdf.settings['Telegram']['group_for_message']
|
||||||
MAIN_PUBLIC = bdf.settings['Telegram']['main_public']
|
MAIN_PUBLIC = bdf.settings['Telegram']['main_public']
|
||||||
|
|||||||
BIN
helper_bot/handlers/private/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
helper_bot/handlers/private/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
helper_bot/handlers/private/__pycache__/main.cpython-312.pyc
Normal file
BIN
helper_bot/handlers/private/__pycache__/main.cpython-312.pyc
Normal file
Binary file not shown.
@@ -1,472 +1,419 @@
|
|||||||
import random
|
import random
|
||||||
import traceback
|
import traceback
|
||||||
import asyncio
|
from datetime import datetime
|
||||||
import html
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from time import sleep
|
||||||
from pathlib import Path
|
|
||||||
|
from aiogram import types, Router, F
|
||||||
from aiogram import types, Router, F
|
from aiogram.filters import Command, StateFilter
|
||||||
from aiogram.filters import Command, StateFilter
|
from aiogram.fsm.context import FSMContext
|
||||||
from aiogram.fsm.context import FSMContext
|
from aiogram.types import FSInputFile
|
||||||
from aiogram.types import FSInputFile
|
|
||||||
|
from helper_bot.filters.main import ChatTypeFilter
|
||||||
from helper_bot.filters.main import ChatTypeFilter
|
from helper_bot.keyboards import get_reply_keyboard, get_reply_keyboard_for_post
|
||||||
from helper_bot.keyboards import get_reply_keyboard, get_reply_keyboard_for_post
|
from helper_bot.keyboards.keyboards import get_reply_keyboard_leave_chat
|
||||||
from helper_bot.keyboards.keyboards import get_reply_keyboard_leave_chat
|
from helper_bot.middlewares.album_middleware import AlbumMiddleware
|
||||||
from helper_bot.middlewares.album_middleware import AlbumMiddleware
|
from helper_bot.middlewares.blacklist_middleware import BlacklistMiddleware
|
||||||
from helper_bot.middlewares.blacklist_middleware import BlacklistMiddleware
|
from helper_bot.utils import messages
|
||||||
from helper_bot.utils import messages
|
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory
|
||||||
from helper_bot.utils.base_dependency_factory import get_global_instance
|
from helper_bot.utils.helper_func import get_first_name, get_text_message, send_text_message, send_photo_message, \
|
||||||
from helper_bot.utils.helper_func import get_first_name, get_text_message, send_text_message, send_photo_message, \
|
send_media_group_message_to_private_chat, prepare_media_group_from_middlewares, send_video_message, \
|
||||||
send_media_group_message_to_private_chat, prepare_media_group_from_middlewares, check_username_and_full_name, \
|
send_video_note_message, send_audio_message, send_voice_message, add_in_db_media, \
|
||||||
send_video_message, send_video_note_message, send_audio_message, send_voice_message, add_in_db_media
|
update_user_info, check_user_emoji
|
||||||
from logs.custom_logger import logger
|
from logs.custom_logger import logger
|
||||||
|
|
||||||
private_router = Router()
|
private_router = Router()
|
||||||
|
|
||||||
private_router.message.middleware(AlbumMiddleware())
|
private_router.message.middleware(AlbumMiddleware())
|
||||||
private_router.message.middleware(BlacklistMiddleware())
|
private_router.message.middleware(BlacklistMiddleware())
|
||||||
|
|
||||||
bdf = get_global_instance()
|
bdf = BaseDependencyFactory()
|
||||||
GROUP_FOR_POST = bdf.settings['Telegram']['group_for_posts']
|
GROUP_FOR_POST = bdf.settings['Telegram']['group_for_posts']
|
||||||
GROUP_FOR_MESSAGE = bdf.settings['Telegram']['group_for_message']
|
GROUP_FOR_MESSAGE = bdf.settings['Telegram']['group_for_message']
|
||||||
MAIN_PUBLIC = bdf.settings['Telegram']['main_public']
|
MAIN_PUBLIC = bdf.settings['Telegram']['main_public']
|
||||||
GROUP_FOR_LOGS = bdf.settings['Telegram']['group_for_logs']
|
GROUP_FOR_LOGS = bdf.settings['Telegram']['group_for_logs']
|
||||||
IMPORTANT_LOGS = bdf.settings['Telegram']['important_logs']
|
IMPORTANT_LOGS = bdf.settings['Telegram']['important_logs']
|
||||||
PREVIEW_LINK = bdf.settings['Telegram']['preview_link']
|
PREVIEW_LINK = bdf.settings['Telegram']['preview_link']
|
||||||
LOGS = bdf.settings['Settings']['logs']
|
LOGS = bdf.settings['Settings']['logs']
|
||||||
TEST = bdf.settings['Settings']['test']
|
TEST = bdf.settings['Settings']['test']
|
||||||
|
|
||||||
BotDB = bdf.get_db()
|
BotDB = bdf.get_db()
|
||||||
|
|
||||||
# Expose sleep for tests (tests patch helper_bot.handlers.private.private_handlers.sleep)
|
|
||||||
sleep = asyncio.sleep
|
@private_router.message(
|
||||||
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
|
Command("emoji")
|
||||||
@private_router.message(
|
)
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
async def handle_emoji_message(message: types.Message, state: FSMContext):
|
||||||
Command("start")
|
await message.forward(chat_id=GROUP_FOR_LOGS)
|
||||||
)
|
user_emoji = check_user_emoji(message)
|
||||||
@private_router.message(
|
await state.set_state("START")
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
if user_emoji is not None:
|
||||||
F.text == 'Вернуться в бота'
|
await message.answer(f'Твоя эмодзя - {user_emoji}', parse_mode='HTML')
|
||||||
)
|
|
||||||
async def handle_start_message(message: types.Message, state: FSMContext):
|
|
||||||
try:
|
@private_router.message(
|
||||||
await message.forward(chat_id=GROUP_FOR_LOGS)
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
full_name = message.from_user.full_name
|
Command("restart")
|
||||||
username = message.from_user.username
|
)
|
||||||
first_name = get_first_name(message)
|
async def handle_restart_message(message: types.Message, state: FSMContext):
|
||||||
is_bot = message.from_user.is_bot
|
try:
|
||||||
language_code = message.from_user.language_code
|
markup = get_reply_keyboard(BotDB, message.from_user.id)
|
||||||
user_id = message.from_user.id
|
await message.forward(chat_id=GROUP_FOR_LOGS)
|
||||||
|
await state.set_state("START")
|
||||||
# Проверяем наличие username для логирования
|
await update_user_info('love', message)
|
||||||
if not username:
|
check_user_emoji(message)
|
||||||
# Экранируем full_name для безопасного использования
|
await message.answer('Я перезапущен!', reply_markup=markup, parse_mode='HTML')
|
||||||
safe_full_name = html.escape(full_name) if full_name else "Неизвестный пользователь"
|
except Exception as e:
|
||||||
await message.bot.send_message(chat_id=GROUP_FOR_LOGS,
|
logger.error(f"Произошла ошибка handle_restart_message. Ошибка:{str(e)}")
|
||||||
text=f'Пользователь {user_id} ({safe_full_name}) обратился к боту без username')
|
await message.bot.send_message(chat_id=IMPORTANT_LOGS,
|
||||||
logger.warning(f"Пользователь {user_id} ({safe_full_name}) обратился к боту без username")
|
text=f"Произошла ошибка handle_restart_message: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
||||||
# Устанавливаем значение по умолчанию для username
|
|
||||||
username = "private_username"
|
|
||||||
|
@private_router.message(
|
||||||
current_date = datetime.now()
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
date = current_date.strftime("%Y-%m-%d %H:%M:%S")
|
Command("start")
|
||||||
if not BotDB.user_exists(user_id):
|
)
|
||||||
BotDB.add_new_user_in_db(user_id, first_name, full_name, username, is_bot, language_code, date,
|
@private_router.message(
|
||||||
date)
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
else:
|
F.text == 'Вернуться в бота'
|
||||||
is_need_update = check_username_and_full_name(user_id, username, full_name, BotDB)
|
)
|
||||||
if is_need_update:
|
async def handle_start_message(message: types.Message, state: FSMContext):
|
||||||
BotDB.update_username_and_full_name(user_id, username, full_name)
|
try:
|
||||||
# Экранируем пользовательские данные для безопасного использования
|
await message.forward(chat_id=GROUP_FOR_LOGS)
|
||||||
safe_full_name = html.escape(full_name) if full_name else "Неизвестный пользователь"
|
await update_user_info('love', message)
|
||||||
safe_username = html.escape(username) if username else "Без никнейма"
|
check_user_emoji(message)
|
||||||
|
await state.set_state("START")
|
||||||
await message.answer(
|
logger.info(
|
||||||
f"Давно не виделись! Вижу что ты изменился;) Теперь буду звать тебя: {safe_full_name} и ник @{safe_username}")
|
f"Формирование приветственного сообщения для пользователя. Сообщение: {message.text} "
|
||||||
await message.bot.send_message(chat_id=GROUP_FOR_LOGS,
|
f"Имя автора сообщения: {message.from_user.full_name})")
|
||||||
text=f'Для пользователя: {user_id} обновлены данные в БД.\nНовое имя: {safe_full_name}\nНовый ник:{safe_username}')
|
name_stick_hello = list(Path('Stick').rglob('Hello_*'))
|
||||||
await asyncio.sleep(1)
|
random_stick_hello = random.choice(name_stick_hello)
|
||||||
BotDB.update_date_for_user(date, user_id)
|
random_stick_hello = FSInputFile(path=random_stick_hello)
|
||||||
await state.set_state("START")
|
logger.info(f"Стикер успешно получен из БД")
|
||||||
logger.info(
|
await message.answer_sticker(random_stick_hello)
|
||||||
f"Формирование приветственного сообщения для пользователя. Сообщение: {message.text} "
|
sleep(0.3)
|
||||||
f"Имя автора сообщения: {message.from_user.full_name})")
|
except Exception as e:
|
||||||
name_stick_hello = list(Path('Stick').rglob('Hello_*'))
|
logger.error(f"Произошла ошибка handle_start_message при получении стикеров. Ошибка:{str(e)}")
|
||||||
random_stick_hello = random.choice(name_stick_hello)
|
await message.bot.send_message(chat_id=IMPORTANT_LOGS,
|
||||||
random_stick_hello = FSInputFile(path=random_stick_hello)
|
text=f"Произошла ошибка при получении стикеров: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
||||||
logger.info(f"Стикер успешно получен из БД")
|
try:
|
||||||
await message.answer_sticker(random_stick_hello)
|
markup = get_reply_keyboard(BotDB, message.from_user.id)
|
||||||
await asyncio.sleep(0.3)
|
hello_message = messages.get_message(get_first_name(message), 'HELLO_MESSAGE')
|
||||||
except Exception as e:
|
await message.answer(hello_message, reply_markup=markup, parse_mode='HTML')
|
||||||
logger.error(f"Произошла ошибка handle_start_message при получении стикеров. Ошибка:{str(e)}")
|
except Exception as e:
|
||||||
await message.bot.send_message(chat_id=IMPORTANT_LOGS,
|
logger.error(
|
||||||
text=f"Произошла ошибка при получении стикеров: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
f"Произошла ошибка при отправке приветственного сообщения для пользователя {message.from_user.id} Имя: {message.from_user.full_name}. Ошибка: {str(e)}")
|
||||||
try:
|
await message.bot.send_message(IMPORTANT_LOGS,
|
||||||
markup = get_reply_keyboard(BotDB, message.from_user.id)
|
f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
||||||
hello_message = messages.get_message(get_first_name(message), 'HELLO_MESSAGE')
|
|
||||||
await message.answer(hello_message, reply_markup=markup, parse_mode='HTML')
|
|
||||||
except Exception as e:
|
@private_router.message(
|
||||||
logger.error(
|
StateFilter("START"),
|
||||||
f"Произошла ошибка при отправке приветственного сообщения для пользователя {message.from_user.id} Имя: {message.from_user.full_name}. Ошибка: {str(e)}")
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
await message.bot.send_message(IMPORTANT_LOGS,
|
F.text == '📢Предложить свой пост'
|
||||||
f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
)
|
||||||
|
async def suggest_post(message: types.Message, state: FSMContext):
|
||||||
|
try:
|
||||||
@private_router.message(
|
user_id = message.from_user.id
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
current_date = datetime.now()
|
||||||
Command("restart")
|
date = current_date.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
)
|
BotDB.update_date_for_user(date, user_id)
|
||||||
async def restart_function(message: types.Message, state: FSMContext):
|
await message.forward(chat_id=GROUP_FOR_LOGS)
|
||||||
await message.forward(chat_id=GROUP_FOR_LOGS)
|
await state.set_state("SUGGEST")
|
||||||
full_name = message.from_user.full_name
|
current_state = await state.get_state()
|
||||||
username = message.from_user.username
|
logger.info(
|
||||||
user_id = message.from_user.id
|
f"Вызов функции suggest_post. Сообщение: {message.text} Имя автора сообщения: {message.from_user.full_name} Идентификатор сообщения: {message.message_id}. State - {current_state}")
|
||||||
|
markup = types.ReplyKeyboardRemove()
|
||||||
# Проверяем наличие username для логирования
|
suggest_news = messages.get_message(get_first_name(message), 'SUGGEST_NEWS')
|
||||||
if not username:
|
await message.answer(suggest_news)
|
||||||
# Экранируем full_name для безопасного использования
|
sleep(0.3)
|
||||||
safe_full_name = html.escape(full_name) if full_name else "Неизвестный пользователь"
|
suggest_news_2 = messages.get_message(get_first_name(message), 'SUGGEST_NEWS_2')
|
||||||
await message.bot.send_message(chat_id=GROUP_FOR_LOGS,
|
await message.answer(suggest_news_2, reply_markup=markup)
|
||||||
text=f'Пользователь {user_id} ({safe_full_name}) обратился к боту без username')
|
except Exception as e:
|
||||||
logger.warning(f"Пользователь {user_id} ({safe_full_name}) обратился к боту без username")
|
await message.bot.send_message(IMPORTANT_LOGS,
|
||||||
# Устанавливаем значение по умолчанию для username
|
f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
||||||
username = "private_username"
|
|
||||||
|
|
||||||
markup = get_reply_keyboard(BotDB, message.from_user.id)
|
@private_router.message(
|
||||||
await message.answer(text='Я перезапущен!',
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
reply_markup=markup)
|
F.text == '👋🏼Сказать пока!'
|
||||||
await state.set_state('START')
|
)
|
||||||
|
@private_router.message(
|
||||||
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
@private_router.message(
|
F.text == 'Выйти из чата'
|
||||||
StateFilter("START"),
|
)
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
async def end_message(message: types.Message, state: FSMContext):
|
||||||
F.text == '📢Предложить свой пост'
|
try:
|
||||||
)
|
user_id = message.from_user.id
|
||||||
async def suggest_post(message: types.Message, state: FSMContext):
|
current_date = datetime.now()
|
||||||
try:
|
date = current_date.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
user_id = message.from_user.id
|
BotDB.update_date_for_user(date, user_id)
|
||||||
current_date = datetime.now()
|
await message.forward(chat_id=GROUP_FOR_LOGS)
|
||||||
date = current_date.strftime("%Y-%m-%d %H:%M:%S")
|
logger.info(
|
||||||
BotDB.update_date_for_user(date, user_id)
|
f"Вызов функции end_message. Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}")
|
||||||
await message.forward(chat_id=GROUP_FOR_LOGS)
|
name_stick_bye = list(Path('Stick').rglob('Universal_*'))
|
||||||
await state.set_state("SUGGEST")
|
random_stick_bye = random.choice(name_stick_bye)
|
||||||
current_state = await state.get_state()
|
random_stick_bye = FSInputFile(path=random_stick_bye)
|
||||||
# Экранируем full_name для безопасного использования в логах
|
await message.answer_sticker(random_stick_bye)
|
||||||
safe_full_name = html.escape(message.from_user.full_name) if message.from_user.full_name else "Неизвестный пользователь"
|
except Exception as e:
|
||||||
logger.info(
|
logger.error(
|
||||||
f"Вызов функции suggest_post. Сообщение: {message.text} Имя автора сообщения: {safe_full_name} Идентификатор сообщения: {message.message_id}. State - {current_state}")
|
f"Ошибка в функции end_message при получении стикера: {str(e)} Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}")
|
||||||
markup = types.ReplyKeyboardRemove()
|
await message.bot.send_message(chat_id=IMPORTANT_LOGS,
|
||||||
suggest_news = messages.get_message(get_first_name(message), 'SUGGEST_NEWS')
|
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
||||||
await message.answer(suggest_news)
|
try:
|
||||||
await asyncio.sleep(0.3)
|
markup = types.ReplyKeyboardRemove()
|
||||||
suggest_news_2 = messages.get_message(get_first_name(message), 'SUGGEST_NEWS_2')
|
bye_message = messages.get_message(get_first_name(message), 'BYE_MESSAGE')
|
||||||
await message.answer(suggest_news_2, reply_markup=markup)
|
await message.answer(bye_message, reply_markup=markup)
|
||||||
except Exception as e:
|
await state.set_state("START")
|
||||||
await message.bot.send_message(IMPORTANT_LOGS,
|
except Exception as e:
|
||||||
f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
logger.error(
|
||||||
|
f"Ошибка в функции stickers при получении сообщения: {str(e)} Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}")
|
||||||
|
await message.bot.send_message(chat_id=IMPORTANT_LOGS,
|
||||||
@private_router.message(
|
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
|
||||||
F.text == '👋🏼Сказать пока!'
|
|
||||||
)
|
@private_router.message(
|
||||||
@private_router.message(
|
StateFilter("SUGGEST"),
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
F.text == 'Выйти из чата'
|
)
|
||||||
)
|
async def suggest_router(message: types.Message, state: FSMContext, album: list = None):
|
||||||
async def end_message(message: types.Message, state: FSMContext):
|
logger.info(
|
||||||
try:
|
f"Вызов функции suggest_router. Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}")
|
||||||
user_id = message.from_user.id
|
first_name = get_first_name(message)
|
||||||
current_date = datetime.now()
|
try:
|
||||||
date = current_date.strftime("%Y-%m-%d %H:%M:%S")
|
post_caption = ''
|
||||||
BotDB.update_date_for_user(date, user_id)
|
if message.media_group_id is not None:
|
||||||
await message.forward(chat_id=GROUP_FOR_LOGS)
|
await send_text_message(GROUP_FOR_LOGS, message,
|
||||||
# Экранируем full_name для безопасного использования в логах
|
f'Закинул медиагруппу, пользователь: имя - {first_name}, ник - {message.from_user.username}')
|
||||||
safe_full_name = html.escape(message.from_user.full_name) if message.from_user.full_name else "Неизвестный пользователь"
|
else:
|
||||||
logger.info(
|
await message.forward(chat_id=GROUP_FOR_LOGS)
|
||||||
f"Вызов функции end_message. Пользователь: {message.from_user.id} Имя автора сообщения: {safe_full_name}")
|
if message.content_type == 'text':
|
||||||
name_stick_bye = list(Path('Stick').rglob('Universal_*'))
|
lower_text = message.text.lower()
|
||||||
random_stick_bye = random.choice(name_stick_bye)
|
# Получаем текст сообщения и преобразовываем его по правилам
|
||||||
random_stick_bye = FSInputFile(path=random_stick_bye)
|
post_text = get_text_message(lower_text, first_name,
|
||||||
await message.answer_sticker(random_stick_bye)
|
message.from_user.username)
|
||||||
except Exception as e:
|
# Получаем клавиатуру для поста
|
||||||
# Экранируем full_name для безопасного использования в логах
|
markup = get_reply_keyboard_for_post()
|
||||||
safe_full_name = html.escape(message.from_user.full_name) if message.from_user.full_name else "Неизвестный пользователь"
|
|
||||||
logger.error(
|
# Отправляем сообщение в приватный канал
|
||||||
f"Ошибка в функции end_message при получении стикера: {str(e)} Пользователь: {message.from_user.id} Имя автора сообщения: {safe_full_name}")
|
sent_message_id = await send_text_message(GROUP_FOR_POST, message, post_text, markup)
|
||||||
await message.bot.send_message(chat_id=IMPORTANT_LOGS,
|
|
||||||
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
# Записываем в базу пост
|
||||||
try:
|
BotDB.add_post_in_db(sent_message_id, message.text, message.from_user.id)
|
||||||
markup = types.ReplyKeyboardRemove()
|
|
||||||
bye_message = messages.get_message(get_first_name(message), 'BYE_MESSAGE')
|
# Отправляем юзеру ответ, что сообщение отравлено и возвращаем его в меню
|
||||||
await message.answer(bye_message, reply_markup=markup)
|
markup_for_user = get_reply_keyboard(BotDB, message.from_user.id)
|
||||||
await state.set_state("START")
|
success_send_message = messages.get_message(first_name, 'SUCCESS_SEND_MESSAGE')
|
||||||
except Exception as e:
|
await message.answer(success_send_message, reply_markup=markup_for_user)
|
||||||
# Экранируем full_name для безопасного использования в логах
|
await state.set_state("START")
|
||||||
safe_full_name = html.escape(message.from_user.full_name) if message.from_user.full_name else "Неизвестный пользователь"
|
|
||||||
logger.error(
|
elif message.content_type == 'photo' and message.media_group_id is None:
|
||||||
f"Ошибка в функции stickers при получении сообщения: {str(e)} Пользователь: {message.from_user.id} Имя автора сообщения: {safe_full_name}")
|
if message.caption:
|
||||||
await message.bot.send_message(chat_id=IMPORTANT_LOGS,
|
lower_caption = message.caption.lower()
|
||||||
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
# Получаем текст сообщения и преобразовываем его по правилам
|
||||||
|
post_caption = get_text_message(lower_caption, first_name,
|
||||||
|
message.from_user.username)
|
||||||
@private_router.message(
|
markup = get_reply_keyboard_for_post()
|
||||||
StateFilter("SUGGEST"),
|
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
# Отправляем фото и текст в приватный канал
|
||||||
)
|
sent_message = await send_photo_message(GROUP_FOR_POST, message,
|
||||||
async def suggest_router(message: types.Message, state: FSMContext, album: list = None):
|
message.photo[-1].file_id, post_caption, markup)
|
||||||
# Экранируем full_name для безопасного использования в логах
|
# Записываем в базу пост и контент
|
||||||
safe_full_name = html.escape(message.from_user.full_name) if message.from_user.full_name else "Неизвестный пользователь"
|
BotDB.add_post_in_db(sent_message.message_id, sent_message.caption, message.from_user.id)
|
||||||
logger.info(
|
await add_in_db_media(sent_message)
|
||||||
f"Вызов функции suggest_router. Пользователь: {message.from_user.id} Имя автора сообщения: {safe_full_name}")
|
|
||||||
first_name = get_first_name(message)
|
# Отправляем юзеру ответ и возвращаем его в меню
|
||||||
try:
|
markup_for_user = get_reply_keyboard(BotDB, message.from_user.id)
|
||||||
post_caption = ''
|
success_send_message = messages.get_message(first_name, 'SUCCESS_SEND_MESSAGE')
|
||||||
if message.media_group_id is not None:
|
await message.answer(success_send_message, reply_markup=markup_for_user)
|
||||||
# Экранируем username для безопасного использования
|
await state.set_state("START")
|
||||||
safe_username = html.escape(message.from_user.username) if message.from_user.username else "Без никнейма"
|
|
||||||
await send_text_message(GROUP_FOR_LOGS, message,
|
elif message.content_type == 'video' and message.media_group_id is None:
|
||||||
f'Закинул медиагруппу, пользователь: имя - {first_name}, ник - {safe_username}')
|
if message.caption:
|
||||||
else:
|
lower_caption = message.caption.lower()
|
||||||
await message.forward(chat_id=GROUP_FOR_LOGS)
|
post_caption = get_text_message(lower_caption, first_name,
|
||||||
if message.content_type == 'text':
|
message.from_user.username)
|
||||||
lower_text = message.text.lower()
|
markup = get_reply_keyboard_for_post()
|
||||||
# Получаем текст сообщения и преобразовываем его по правилам
|
# Получаем текст сообщения и преобразовываем его по правилам
|
||||||
post_text = get_text_message(lower_text, first_name,
|
|
||||||
message.from_user.username)
|
# Отправляем видео и текст в приватный канал
|
||||||
# Получаем клавиатуру для поста
|
sent_message = await send_video_message(GROUP_FOR_POST, message,
|
||||||
markup = get_reply_keyboard_for_post()
|
message.video.file_id, post_caption, markup)
|
||||||
|
|
||||||
# Отправляем сообщение в приватный канал
|
# Записываем в базу пост и контент
|
||||||
sent_message_id = await send_text_message(GROUP_FOR_POST, message, post_text, markup)
|
BotDB.add_post_in_db(sent_message.message_id, sent_message.caption, message.from_user.id)
|
||||||
|
await add_in_db_media(sent_message)
|
||||||
# Записываем в базу пост
|
|
||||||
BotDB.add_post_in_db(sent_message_id, message.text, message.from_user.id)
|
# Записываем в базу пост и контент
|
||||||
|
BotDB.add_post_in_db(sent_message.message_id, sent_message.caption, message.from_user.id)
|
||||||
# Отправляем юзеру ответ, что сообщение отравлено и возвращаем его в меню
|
await add_in_db_media(sent_message)
|
||||||
markup_for_user = get_reply_keyboard(BotDB, message.from_user.id)
|
|
||||||
success_send_message = messages.get_message(first_name, 'SUCCESS_SEND_MESSAGE')
|
# Отправляем юзеру ответ и возвращаем его в меню
|
||||||
await message.answer(success_send_message, reply_markup=markup_for_user)
|
markup_for_user = get_reply_keyboard(BotDB, message.from_user.id)
|
||||||
await state.set_state("START")
|
success_send_message = messages.get_message(first_name, 'SUCCESS_SEND_MESSAGE')
|
||||||
|
await message.answer(success_send_message, reply_markup=markup_for_user)
|
||||||
elif message.content_type == 'photo' and message.media_group_id is None:
|
await state.set_state("START")
|
||||||
if message.caption:
|
|
||||||
lower_caption = message.caption.lower()
|
elif message.content_type == 'video_note' and message.media_group_id is None:
|
||||||
# Получаем текст сообщения и преобразовываем его по правилам
|
markup = get_reply_keyboard_for_post()
|
||||||
post_caption = get_text_message(lower_caption, first_name,
|
|
||||||
message.from_user.username)
|
# Отправляем видеокружок в приватный канал
|
||||||
markup = get_reply_keyboard_for_post()
|
sent_message = await send_video_note_message(GROUP_FOR_POST, message,
|
||||||
|
message.video_note.file_id, markup)
|
||||||
# Отправляем фото и текст в приватный канал
|
|
||||||
sent_message = await send_photo_message(GROUP_FOR_POST, message,
|
# Записываем в базу пост и контент
|
||||||
message.photo[-1].file_id, post_caption, markup)
|
BotDB.add_post_in_db(sent_message.message_id, sent_message.caption, message.from_user.id)
|
||||||
# Записываем в базу пост и контент
|
await add_in_db_media(sent_message)
|
||||||
BotDB.add_post_in_db(sent_message.message_id, sent_message.caption, message.from_user.id)
|
|
||||||
await add_in_db_media(sent_message, BotDB)
|
# Отправляем юзеру ответ и возвращаем его в меню
|
||||||
|
markup_for_user = get_reply_keyboard(BotDB, message.from_user.id)
|
||||||
# Отправляем юзеру ответ и возвращаем его в меню
|
success_send_message = messages.get_message(first_name, 'SUCCESS_SEND_MESSAGE')
|
||||||
markup_for_user = get_reply_keyboard(BotDB, message.from_user.id)
|
await message.answer(success_send_message, reply_markup=markup_for_user)
|
||||||
success_send_message = messages.get_message(first_name, 'SUCCESS_SEND_MESSAGE')
|
await state.set_state("START")
|
||||||
await message.answer(success_send_message, reply_markup=markup_for_user)
|
|
||||||
await state.set_state("START")
|
elif message.content_type == 'audio' and message.media_group_id is None:
|
||||||
|
if message.caption:
|
||||||
elif message.content_type == 'video' and message.media_group_id is None:
|
lower_caption = message.caption.lower()
|
||||||
if message.caption:
|
# Получаем текст сообщения и преобразовываем его по правилам
|
||||||
lower_caption = message.caption.lower()
|
post_caption = get_text_message(lower_caption, first_name,
|
||||||
post_caption = get_text_message(lower_caption, first_name,
|
message.from_user.username)
|
||||||
message.from_user.username)
|
markup = get_reply_keyboard_for_post()
|
||||||
markup = get_reply_keyboard_for_post()
|
|
||||||
# Получаем текст сообщения и преобразовываем его по правилам
|
# Отправляем аудио и текст в приватный канал
|
||||||
|
sent_message = await send_audio_message(GROUP_FOR_POST, message,
|
||||||
# Отправляем видео и текст в приватный канал
|
message.audio.file_id, post_caption, markup)
|
||||||
sent_message = await send_video_message(GROUP_FOR_POST, message,
|
|
||||||
message.video.file_id, post_caption, markup)
|
# Записываем в базу пост и контент
|
||||||
|
BotDB.add_post_in_db(sent_message.message_id, sent_message.caption, message.from_user.id)
|
||||||
# Записываем в базу пост и контент
|
await add_in_db_media(sent_message)
|
||||||
BotDB.add_post_in_db(sent_message.message_id, sent_message.caption, message.from_user.id)
|
|
||||||
await add_in_db_media(sent_message, BotDB)
|
# Отправляем юзеру ответ и возвращаем его в меню
|
||||||
|
markup_for_user = get_reply_keyboard(BotDB, message.from_user.id)
|
||||||
# Записываем в базу пост и контент
|
success_send_message = messages.get_message(first_name, 'SUCCESS_SEND_MESSAGE')
|
||||||
BotDB.add_post_in_db(sent_message.message_id, sent_message.caption, message.from_user.id)
|
await message.answer(success_send_message, reply_markup=markup_for_user)
|
||||||
await add_in_db_media(sent_message)
|
await state.set_state("START")
|
||||||
|
|
||||||
# Отправляем юзеру ответ и возвращаем его в меню
|
elif message.content_type == 'voice' and message.media_group_id is None:
|
||||||
markup_for_user = get_reply_keyboard(BotDB, message.from_user.id)
|
markup = get_reply_keyboard_for_post()
|
||||||
success_send_message = messages.get_message(first_name, 'SUCCESS_SEND_MESSAGE')
|
|
||||||
await message.answer(success_send_message, reply_markup=markup_for_user)
|
# Отправляем войс и текст в приватный канал
|
||||||
await state.set_state("START")
|
sent_message = await send_voice_message(GROUP_FOR_POST, message,
|
||||||
|
message.voice.file_id, markup)
|
||||||
elif message.content_type == 'video_note' and message.media_group_id is None:
|
|
||||||
markup = get_reply_keyboard_for_post()
|
# Записываем в базу пост и контент
|
||||||
|
BotDB.add_post_in_db(sent_message.message_id, sent_message.caption, message.from_user.id)
|
||||||
# Отправляем видеокружок в приватный канал
|
await add_in_db_media(sent_message)
|
||||||
sent_message = await send_video_note_message(GROUP_FOR_POST, message,
|
|
||||||
message.video_note.file_id, markup)
|
# Отправляем юзеру ответ и возвращаем его в меню
|
||||||
|
markup_for_user = get_reply_keyboard(BotDB, message.from_user.id)
|
||||||
# Записываем в базу пост и контент
|
success_send_message = messages.get_message(first_name, 'SUCCESS_SEND_MESSAGE')
|
||||||
BotDB.add_post_in_db(sent_message.message_id, sent_message.caption, message.from_user.id)
|
await message.answer(success_send_message, reply_markup=markup_for_user)
|
||||||
await add_in_db_media(sent_message, BotDB)
|
await state.set_state("START")
|
||||||
|
|
||||||
# Отправляем юзеру ответ и возвращаем его в меню
|
elif message.media_group_id is not None:
|
||||||
markup_for_user = get_reply_keyboard(BotDB, message.from_user.id)
|
post_caption = " "
|
||||||
success_send_message = messages.get_message(first_name, 'SUCCESS_SEND_MESSAGE')
|
|
||||||
await message.answer(success_send_message, reply_markup=markup_for_user)
|
# Получаем сообщение и проверяем есть ли подпись. Если подпись есть, то преобразуем ее через функцию
|
||||||
await state.set_state("START")
|
if album[0].caption:
|
||||||
|
lower_caption = album[0].caption.lower()
|
||||||
elif message.content_type == 'audio' and message.media_group_id is None:
|
post_caption = get_text_message(lower_caption, first_name,
|
||||||
if message.caption:
|
message.from_user.username)
|
||||||
lower_caption = message.caption.lower()
|
|
||||||
# Получаем текст сообщения и преобразовываем его по правилам
|
# Иначе обрабатываем фото и получаем медиагруппу
|
||||||
post_caption = get_text_message(lower_caption, first_name,
|
media_group = await prepare_media_group_from_middlewares(album, post_caption)
|
||||||
message.from_user.username)
|
|
||||||
markup = get_reply_keyboard_for_post()
|
# Отправляем медиагруппу в секретный чат
|
||||||
|
media_group_message_id = await send_media_group_message_to_private_chat(GROUP_FOR_POST, message,
|
||||||
# Отправляем аудио и текст в приватный канал
|
media_group)
|
||||||
sent_message = await send_audio_message(GROUP_FOR_POST, message,
|
sleep(0.2)
|
||||||
message.audio.file_id, post_caption, markup)
|
|
||||||
|
# Получаем клавиатуру и отправляем еще одно текстовое сообщение с кнопками
|
||||||
# Записываем в базу пост и контент
|
markup = get_reply_keyboard_for_post()
|
||||||
BotDB.add_post_in_db(sent_message.message_id, sent_message.caption, message.from_user.id)
|
help_message_id = await send_text_message(GROUP_FOR_POST, message, "^", markup)
|
||||||
await add_in_db_media(sent_message, BotDB)
|
|
||||||
|
# Записываем в state идентификаторы текстового сообщения И последнего сообщения медиагруппы
|
||||||
# Отправляем юзеру ответ и возвращаем его в меню
|
BotDB.update_helper_message_in_db(message_id=media_group_message_id, helper_message_id=help_message_id)
|
||||||
markup_for_user = get_reply_keyboard(BotDB, message.from_user.id)
|
|
||||||
success_send_message = messages.get_message(first_name, 'SUCCESS_SEND_MESSAGE')
|
# Получаем клавиатуру для пользователя, благодарим за пост, и возвращаем в дефолтное сообщение
|
||||||
await message.answer(success_send_message, reply_markup=markup_for_user)
|
markup_for_user = get_reply_keyboard(BotDB, message.from_user.id)
|
||||||
await state.set_state("START")
|
success_send_message = messages.get_message(first_name, 'SUCCESS_SEND_MESSAGE')
|
||||||
|
await message.answer(success_send_message, reply_markup=markup_for_user)
|
||||||
elif message.content_type == 'voice' and message.media_group_id is None:
|
await state.set_state("START")
|
||||||
markup = get_reply_keyboard_for_post()
|
else:
|
||||||
|
await message.bot.send_message(message.chat.id,
|
||||||
# Отправляем войс и текст в приватный канал
|
'Я пока не умею работать с таким сообщением. '
|
||||||
sent_message = await send_voice_message(GROUP_FOR_POST, message,
|
'Пришли текст и фото/фоты(ы). А лучше перешли это сообщение админу @kerrad1\n'
|
||||||
message.voice.file_id, markup)
|
'Мы добавим его к обработке если необходимо')
|
||||||
|
except Exception as e:
|
||||||
# Записываем в базу пост и контент
|
await message.bot.send_message(chat_id=IMPORTANT_LOGS,
|
||||||
BotDB.add_post_in_db(sent_message.message_id, sent_message.caption, message.from_user.id)
|
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
||||||
await add_in_db_media(sent_message, BotDB)
|
|
||||||
|
|
||||||
# Отправляем юзеру ответ и возвращаем его в меню
|
@private_router.message(
|
||||||
markup_for_user = get_reply_keyboard(BotDB, message.from_user.id)
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
success_send_message = messages.get_message(first_name, 'SUCCESS_SEND_MESSAGE')
|
F.text == '🤪Хочу стикеры'
|
||||||
await message.answer(success_send_message, reply_markup=markup_for_user)
|
)
|
||||||
await state.set_state("START")
|
async def stickers(message: types.Message, state: FSMContext):
|
||||||
|
logger.info(
|
||||||
elif message.media_group_id is not None:
|
f"Вызов функции stickers. Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}")
|
||||||
post_caption = " "
|
markup = get_reply_keyboard(BotDB, message.from_user.id)
|
||||||
|
try:
|
||||||
# Получаем сообщение и проверяем есть ли подпись. Если подпись есть, то преобразуем ее через функцию
|
BotDB.update_info_about_stickers(user_id=message.from_user.id)
|
||||||
if album[0].caption:
|
await message.forward(chat_id=GROUP_FOR_LOGS)
|
||||||
lower_caption = album[0].caption.lower()
|
await message.answer(text='Хорошо, лови, добавить можно отсюда: https://t.me/addstickers/love_biysk',
|
||||||
post_caption = get_text_message(lower_caption, first_name,
|
reply_markup=markup)
|
||||||
message.from_user.username)
|
await state.set_state("START")
|
||||||
|
except Exception as e:
|
||||||
# Иначе обрабатываем фото и получаем медиагруппу
|
await message.bot.send_message(chat_id=IMPORTANT_LOGS,
|
||||||
media_group = await prepare_media_group_from_middlewares(album, post_caption)
|
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
||||||
|
logger.error(
|
||||||
# Отправляем медиагруппу в секретный чат
|
f"Ошибка функции stickers. Ошибка: {str(e)} Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}")
|
||||||
media_group_message_id = await send_media_group_message_to_private_chat(GROUP_FOR_POST, message,
|
|
||||||
media_group, BotDB)
|
|
||||||
await asyncio.sleep(0.2)
|
@private_router.message(
|
||||||
|
StateFilter("START"),
|
||||||
# Получаем клавиатуру и отправляем еще одно текстовое сообщение с кнопками
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
markup = get_reply_keyboard_for_post()
|
F.text == '📩Связаться с админами'
|
||||||
help_message_id = await send_text_message(GROUP_FOR_POST, message, "^", markup)
|
)
|
||||||
|
async def connect_with_admin(message: types.Message, state: FSMContext):
|
||||||
# Записываем в state идентификаторы текстового сообщения И последнего сообщения медиагруппы
|
logger.info(
|
||||||
BotDB.update_helper_message_in_db(message_id=media_group_message_id, helper_message_id=help_message_id)
|
f"Вызов функции connect_with_admin. Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}")
|
||||||
|
user_id = message.from_user.id
|
||||||
# Получаем клавиатуру для пользователя, благодарим за пост, и возвращаем в дефолтное сообщение
|
current_date = datetime.now()
|
||||||
markup_for_user = get_reply_keyboard(BotDB, message.from_user.id)
|
date = current_date.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
success_send_message = messages.get_message(first_name, 'SUCCESS_SEND_MESSAGE')
|
BotDB.update_date_for_user(date, user_id)
|
||||||
await message.answer(success_send_message, reply_markup=markup_for_user)
|
admin_message = messages.get_message(get_first_name(message), 'CONNECT_WITH_ADMIN')
|
||||||
await state.set_state("START")
|
await message.answer(admin_message, parse_mode="html")
|
||||||
else:
|
await message.forward(chat_id=GROUP_FOR_LOGS)
|
||||||
await message.bot.send_message(message.chat.id,
|
await state.set_state("PRE_CHAT")
|
||||||
'Я пока не умею работать с таким сообщением. '
|
|
||||||
'Пришли текст и фото/фоты(ы). А лучше перешли это сообщение админу @kerrad1\n'
|
|
||||||
'Мы добавим его к обработке если необходимо')
|
@private_router.message(
|
||||||
except Exception as e:
|
StateFilter("PRE_CHAT"),
|
||||||
await message.bot.send_message(chat_id=IMPORTANT_LOGS,
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
)
|
||||||
|
@private_router.message(
|
||||||
|
StateFilter("CHAT"),
|
||||||
@private_router.message(
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
)
|
||||||
F.text == '🤪Хочу стикеры'
|
async def resend_message_in_group_for_message(message: types.Message, state: FSMContext):
|
||||||
)
|
user_id = message.from_user.id
|
||||||
async def stickers(message: types.Message, state: FSMContext):
|
current_date = datetime.now()
|
||||||
# Экранируем full_name для безопасного использования в логах
|
date = current_date.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
safe_full_name = html.escape(message.from_user.full_name) if message.from_user.full_name else "Неизвестный пользователь"
|
BotDB.update_date_for_user(date, user_id)
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Вызов функции stickers. Пользователь: {message.from_user.id} Имя автора сообщения: {safe_full_name}")
|
f"Попытка пересылки сообщения в связь с админами. Сообщение: {message.text} Имя автора сообщения: {message.from_user.full_name} Идентификатор сообщения: {message.message_id})")
|
||||||
markup = get_reply_keyboard(BotDB, message.from_user.id)
|
await message.forward(chat_id=GROUP_FOR_MESSAGE)
|
||||||
try:
|
current_date = datetime.now()
|
||||||
BotDB.update_info_about_stickers(user_id=message.from_user.id)
|
date = current_date.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
await message.forward(chat_id=GROUP_FOR_LOGS)
|
BotDB.add_new_message_in_db(message.text, message.from_user.id, message.message_id + 1, date)
|
||||||
await message.answer(text='Хорошо, лови, добавить можно отсюда: https://t.me/addstickers/love_biysk',
|
question = messages.get_message(get_first_name(message), 'QUESTION')
|
||||||
reply_markup=markup)
|
user_state = await state.get_state()
|
||||||
await state.set_state("START")
|
if user_state == "PRE_CHAT":
|
||||||
except Exception as e:
|
markup = get_reply_keyboard(BotDB, message.from_user.id)
|
||||||
await message.bot.send_message(chat_id=IMPORTANT_LOGS,
|
await message.answer(question, reply_markup=markup)
|
||||||
text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
|
await state.set_state("START")
|
||||||
# Экранируем full_name для безопасного использования в логах
|
elif user_state == "CHAT":
|
||||||
safe_full_name = html.escape(message.from_user.full_name) if message.from_user.full_name else "Неизвестный пользователь"
|
markup = get_reply_keyboard_leave_chat()
|
||||||
logger.error(
|
await message.answer(question, reply_markup=markup)
|
||||||
f"Ошибка функции stickers. Ошибка: {str(e)} Пользователь: {message.from_user.id} Имя автора сообщения: {safe_full_name}")
|
|
||||||
|
|
||||||
|
|
||||||
@private_router.message(
|
|
||||||
StateFilter("START"),
|
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
|
||||||
F.text == '📩Связаться с админами'
|
|
||||||
)
|
|
||||||
async def connect_with_admin(message: types.Message, state: FSMContext):
|
|
||||||
# Экранируем full_name для безопасного использования в логах
|
|
||||||
safe_full_name = html.escape(message.from_user.full_name) if message.from_user.full_name else "Неизвестный пользователь"
|
|
||||||
logger.info(
|
|
||||||
f"Вызов функции connect_with_admin. Пользователь: {message.from_user.id} Имя автора сообщения: {safe_full_name}")
|
|
||||||
user_id = message.from_user.id
|
|
||||||
current_date = datetime.now()
|
|
||||||
date = current_date.strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
BotDB.update_date_for_user(date, user_id)
|
|
||||||
admin_message = messages.get_message(get_first_name(message), 'CONNECT_WITH_ADMIN')
|
|
||||||
await message.answer(admin_message, parse_mode="html")
|
|
||||||
await message.forward(chat_id=GROUP_FOR_LOGS)
|
|
||||||
await state.set_state("PRE_CHAT")
|
|
||||||
|
|
||||||
|
|
||||||
@private_router.message(
|
|
||||||
StateFilter("PRE_CHAT"),
|
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
|
||||||
)
|
|
||||||
@private_router.message(
|
|
||||||
StateFilter("CHAT"),
|
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
|
||||||
)
|
|
||||||
async def resend_message_in_group_for_message(message: types.Message, state: FSMContext):
|
|
||||||
user_id = message.from_user.id
|
|
||||||
current_date = datetime.now()
|
|
||||||
date = current_date.strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
BotDB.update_date_for_user(date, user_id)
|
|
||||||
# Экранируем full_name для безопасного использования в логах
|
|
||||||
safe_full_name = html.escape(message.from_user.full_name) if message.from_user.full_name else "Неизвестный пользователь"
|
|
||||||
logger.info(
|
|
||||||
f"Попытка пересылки сообщения в связь с админами. Сообщение: {message.text} Имя автора сообщения: {safe_full_name} Идентификатор сообщения: {message.message_id})")
|
|
||||||
await message.forward(chat_id=GROUP_FOR_MESSAGE)
|
|
||||||
current_date = datetime.now()
|
|
||||||
date = current_date.strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
BotDB.add_new_message_in_db(message.text, message.from_user.id, message.message_id + 1, date)
|
|
||||||
question = messages.get_message(get_first_name(message), 'QUESTION')
|
|
||||||
user_state = await state.get_state()
|
|
||||||
if user_state == "PRE_CHAT":
|
|
||||||
markup = get_reply_keyboard(BotDB, message.from_user.id)
|
|
||||||
await message.answer(question, reply_markup=markup)
|
|
||||||
await state.set_state("START")
|
|
||||||
elif user_state == "CHAT":
|
|
||||||
markup = get_reply_keyboard_leave_chat()
|
|
||||||
await message.answer(question, reply_markup=markup)
|
|
||||||
|
|||||||
BIN
helper_bot/keyboards/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
helper_bot/keyboards/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
@@ -36,8 +36,6 @@ def get_reply_keyboard_admin():
|
|||||||
builder = ReplyKeyboardBuilder()
|
builder = ReplyKeyboardBuilder()
|
||||||
builder.add(types.KeyboardButton(text="Бан (Список)"))
|
builder.add(types.KeyboardButton(text="Бан (Список)"))
|
||||||
builder.add(types.KeyboardButton(text="Бан по нику"))
|
builder.add(types.KeyboardButton(text="Бан по нику"))
|
||||||
builder.add(types.KeyboardButton(text="Бан по ID"))
|
|
||||||
builder.add(types.KeyboardButton(text="Тестовый бан"))
|
|
||||||
builder.add(types.KeyboardButton(text="Разбан (список)"))
|
builder.add(types.KeyboardButton(text="Разбан (список)"))
|
||||||
builder.add(types.KeyboardButton(text="Вернуться в бота"))
|
builder.add(types.KeyboardButton(text="Вернуться в бота"))
|
||||||
markup = builder.as_markup(resize_keyboard=True, one_time_keyboard=True)
|
markup = builder.as_markup(resize_keyboard=True, one_time_keyboard=True)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ async def start_bot(bdf):
|
|||||||
bot = Bot(token=token, default=DefaultBotProperties(
|
bot = Bot(token=token, default=DefaultBotProperties(
|
||||||
parse_mode='HTML',
|
parse_mode='HTML',
|
||||||
link_preview_is_disabled=bdf.settings['Telegram']['preview_link']
|
link_preview_is_disabled=bdf.settings['Telegram']['preview_link']
|
||||||
), timeout=30.0) # Добавляем таймаут для предотвращения зависаний
|
))
|
||||||
dp = Dispatcher(storage=MemoryStorage(), fsm_strategy=FSMStrategy.GLOBAL_USER)
|
dp = Dispatcher(storage=MemoryStorage(), fsm_strategy=FSMStrategy.GLOBAL_USER)
|
||||||
dp.include_routers(private_router, callback_router, group_router, admin_router)
|
dp.include_routers(private_router, callback_router, group_router, admin_router)
|
||||||
await bot.delete_webhook(drop_pending_updates=True)
|
await bot.delete_webhook(drop_pending_updates=True)
|
||||||
|
|||||||
BIN
helper_bot/middlewares/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
helper_bot/middlewares/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
@@ -6,7 +6,7 @@ from aiogram.types import Message
|
|||||||
|
|
||||||
|
|
||||||
class AlbumMiddleware(BaseMiddleware):
|
class AlbumMiddleware(BaseMiddleware):
|
||||||
def __init__(self, latency: Union[int, float] = 0.01): # Уменьшено с 0.1 до 0.01
|
def __init__(self, latency: Union[int, float] = 0.1):
|
||||||
# Initialize latency and album_data dictionary
|
# Initialize latency and album_data dictionary
|
||||||
self.latency = latency
|
self.latency = latency
|
||||||
self.album_data = {}
|
self.album_data = {}
|
||||||
|
|||||||
@@ -1,26 +1,21 @@
|
|||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
import html
|
|
||||||
|
|
||||||
from aiogram import BaseMiddleware, types
|
from aiogram import BaseMiddleware, types
|
||||||
from helper_bot.utils.base_dependency_factory import get_global_instance
|
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory
|
||||||
from logs.custom_logger import logger
|
from logs.custom_logger import logger
|
||||||
|
|
||||||
bdf = get_global_instance()
|
bdf = BaseDependencyFactory()
|
||||||
BotDB = bdf.get_db()
|
BotDB = bdf.get_db()
|
||||||
|
|
||||||
|
|
||||||
class BlacklistMiddleware(BaseMiddleware):
|
class BlacklistMiddleware(BaseMiddleware):
|
||||||
async def __call__(self, handler, event: types.Message, data: Dict[str, Any]) -> Any:
|
async def __call__(self, handler, event: types.Message, data: Dict[str, Any]) -> Any:
|
||||||
logger.info(f'Вызов BlacklistMiddleware для пользователя {event.from_user.username}')
|
logger.info(f'Вызов BlacklistMiddleware для пользователя {event.from_user.username}')
|
||||||
# Используем асинхронную версию для предотвращения блокировки
|
if BotDB.check_user_in_blacklist(user_id=event.from_user.id):
|
||||||
if await BotDB.check_user_in_blacklist_async(user_id=event.from_user.id):
|
|
||||||
logger.info(f'BlacklistMiddleware результат для пользователя: {event.from_user.username} заблокирован!')
|
logger.info(f'BlacklistMiddleware результат для пользователя: {event.from_user.username} заблокирован!')
|
||||||
user_info = await BotDB.get_blacklist_users_by_id_async(event.from_user.id)
|
user_info = BotDB.get_blacklist_users_by_id(event.from_user.id)
|
||||||
# Экранируем потенциально проблемные символы
|
|
||||||
reason = html.escape(str(user_info[2])) if user_info[2] else "Не указана"
|
|
||||||
date_unban = html.escape(str(user_info[3])) if user_info[3] else "Не указана"
|
|
||||||
await event.answer(
|
await event.answer(
|
||||||
f"<b>Ты заблокирован.</b>\n<b>Причина блокировки:</b> {reason}\n<b>Дата разбана:</b> {date_unban}")
|
f"<b>Ты заблокирован.</b>\n<b>Причина блокировки:</b> {user_info[2]}\n<b>Дата разбана:</b> {user_info[3]}")
|
||||||
return False
|
return False
|
||||||
logger.info(f'BlacklistMiddleware результат для пользователя: {event.from_user.username} доступ разрешен')
|
logger.info(f'BlacklistMiddleware результат для пользователя: {event.from_user.username} доступ разрешен')
|
||||||
return await handler(event, data)
|
return await handler(event, data)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from aiogram.types import Message
|
|||||||
|
|
||||||
|
|
||||||
class BulkTextMiddleware(BaseMiddleware):
|
class BulkTextMiddleware(BaseMiddleware):
|
||||||
def __init__(self, latency: Union[int, float] = 0.01): # Уменьшено с 0.1 до 0.01
|
def __init__(self, latency: Union[int, float] = 0.1):
|
||||||
# Initialize latency and album_data dictionary
|
# Initialize latency and album_data dictionary
|
||||||
self.latency = latency
|
self.latency = latency
|
||||||
self.texts = defaultdict(list)
|
self.texts = defaultdict(list)
|
||||||
|
|||||||
BIN
helper_bot/utils/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
helper_bot/utils/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
helper_bot/utils/__pycache__/helper_func.cpython-312.pyc
Normal file
BIN
helper_bot/utils/__pycache__/helper_func.cpython-312.pyc
Normal file
Binary file not shown.
BIN
helper_bot/utils/__pycache__/messages.cpython-312.pyc
Normal file
BIN
helper_bot/utils/__pycache__/messages.cpython-312.pyc
Normal file
Binary file not shown.
BIN
helper_bot/utils/__pycache__/state.cpython-312.pyc
Normal file
BIN
helper_bot/utils/__pycache__/state.cpython-312.pyc
Normal file
Binary file not shown.
@@ -1,46 +1,35 @@
|
|||||||
import configparser
|
import configparser
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from database.db import BotDB
|
from database.db import BotDB
|
||||||
|
|
||||||
current_dir = os.getcwd()
|
current_dir = os.getcwd()
|
||||||
|
|
||||||
|
|
||||||
class BaseDependencyFactory:
|
class BaseDependencyFactory:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# Загрузка настроек из settings.ini
|
# Загрузка настроек из settings.ini
|
||||||
config_path = os.path.join(sys.path[0], 'settings.ini')
|
config_path = os.path.join(sys.path[0], 'settings.ini')
|
||||||
self.config = configparser.ConfigParser()
|
self.config = configparser.ConfigParser()
|
||||||
self.config.read(config_path)
|
self.config.read(config_path)
|
||||||
self.settings = {}
|
self.settings = {}
|
||||||
self.database = BotDB(current_dir, 'tg-bot-database.db')
|
self.database = BotDB(current_dir, 'database/tg-bot-database')
|
||||||
|
|
||||||
for section in self.config.sections():
|
for section in self.config.sections():
|
||||||
self.settings[section] = {}
|
self.settings[section] = {}
|
||||||
for key in self.config[section]:
|
for key in self.config[section]:
|
||||||
# Преобразование значений в соответствующий тип
|
# Преобразование значений в соответствующий тип
|
||||||
if key == 'PREVIEW_LINK':
|
if key == 'PREVIEW_LINK':
|
||||||
self.settings[section][key] = self.config.getboolean(section, key)
|
self.settings[section][key] = self.config.getboolean(section, key)
|
||||||
elif key == 'LOGS' or key == 'TEST':
|
elif key == 'LOGS' or key == 'TEST':
|
||||||
self.settings[section][key] = self.config.getboolean(section, key)
|
self.settings[section][key] = self.config.getboolean(section, key)
|
||||||
else:
|
else:
|
||||||
self.settings[section][key] = self.config.get(section, key)
|
self.settings[section][key] = self.config.get(section, key)
|
||||||
|
|
||||||
def get_settings(self):
|
def get_settings(self):
|
||||||
return self.settings
|
return self.settings
|
||||||
|
|
||||||
def get_db(self) -> BotDB:
|
def get_db(self) -> BotDB:
|
||||||
"""Возвращает подключение к базе данных."""
|
"""Возвращает подключение к базе данных."""
|
||||||
return self.database
|
return self.database
|
||||||
|
|
||||||
|
|
||||||
# Создаем единый экземпляр для всего приложения
|
|
||||||
_global_instance = None
|
|
||||||
|
|
||||||
def get_global_instance():
|
|
||||||
"""Возвращает глобальный экземпляр BaseDependencyFactory."""
|
|
||||||
global _global_instance
|
|
||||||
if _global_instance is None:
|
|
||||||
_global_instance = BaseDependencyFactory()
|
|
||||||
return _global_instance
|
|
||||||
|
|||||||
@@ -1,460 +1,444 @@
|
|||||||
import html
|
import html
|
||||||
import os
|
import os
|
||||||
from datetime import datetime, timedelta
|
import random
|
||||||
|
from datetime import datetime, timedelta
|
||||||
from aiogram import types
|
from time import sleep
|
||||||
from aiogram.types import InputMediaPhoto, FSInputFile, InputMediaVideo, InputMediaAudio
|
|
||||||
|
import emoji
|
||||||
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory
|
from aiogram import types
|
||||||
from logs.custom_logger import logger
|
from aiogram.types import InputMediaPhoto, FSInputFile, InputMediaVideo, InputMediaAudio
|
||||||
|
|
||||||
|
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory
|
||||||
def safe_html_escape(text: str) -> str:
|
from logs.custom_logger import logger
|
||||||
"""
|
|
||||||
Безопасно экранирует текст для использования в HTML разметке.
|
bdf = BaseDependencyFactory()
|
||||||
|
GROUP_FOR_LOGS = bdf.settings['Telegram']['group_for_logs']
|
||||||
Args:
|
|
||||||
text: Текст для экранирования
|
emoji_list = list(emoji.EMOJI_DATA.keys())
|
||||||
|
|
||||||
Returns:
|
BotDB = bdf.get_db()
|
||||||
str: Экранированный текст
|
|
||||||
"""
|
|
||||||
if text is None:
|
def get_first_name(message: types.Message) -> str:
|
||||||
return ""
|
first_name = html.escape(message.from_user.first_name)
|
||||||
return html.escape(str(text))
|
return first_name
|
||||||
|
|
||||||
|
|
||||||
def get_first_name(message: types.Message) -> str:
|
def get_text_message(post_text: str, first_name: str, username: str):
|
||||||
"""
|
"""
|
||||||
Безопасно получает и экранирует имя пользователя для использования в HTML разметке.
|
Форматирует текст сообщения для публикации в зависимости от наличия ключевых слов "анон" и "неанон".
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
message: Сообщение от пользователя
|
post_text: Текст сообщения
|
||||||
|
first_name: Имя автора поста
|
||||||
Returns:
|
username: Юзернейм автора поста
|
||||||
str: Экранированное имя пользователя или пустая строка если имя отсутствует
|
|
||||||
"""
|
Returns:
|
||||||
if message.from_user.first_name is None:
|
str: - Сформированный текст сообщения.
|
||||||
# Поведение ожидаемое тестами: поднимать AttributeError при None
|
"""
|
||||||
raise AttributeError("first_name is None")
|
if "неанон" in post_text or "не анон" in post_text:
|
||||||
if message.from_user.first_name:
|
return f'Пост из ТГ:\n{post_text}\n\nАвтор поста: {first_name} @{username}'
|
||||||
# Дополнительная проверка на специальные символы, которые могут вызвать проблемы в HTML
|
elif "анон" in post_text:
|
||||||
first_name = str(message.from_user.first_name)
|
return f'Пост из ТГ:\n{post_text}\n\nПост опубликован анонимно'
|
||||||
# Удаляем или заменяем потенциально проблемные символы
|
else:
|
||||||
first_name = first_name.replace('\u0cc0', '') # Убираем символ "ೀ" (U+0CC0)
|
return f'Пост из ТГ:\n{post_text}\n\nАвтор поста: {first_name} @{username}'
|
||||||
first_name = first_name.replace('\u0cc1', '') # Убираем символ "ೀ" (U+0CC1)
|
|
||||||
first_name = html.escape(first_name)
|
|
||||||
return first_name
|
async def download_file(message: types.Message, file_id: str):
|
||||||
return ""
|
"""
|
||||||
|
Скачивает файл по file_id из Telegram.
|
||||||
|
|
||||||
def get_text_message(post_text: str, first_name: str, username: str = None):
|
Args:
|
||||||
"""
|
message: сообщение
|
||||||
Форматирует текст сообщения для публикации в зависимости от наличия ключевых слов "анон" и "неанон".
|
file_id: File ID фотографии
|
||||||
|
filename: Имя файла, под которым будет сохранено фото
|
||||||
Args:
|
|
||||||
post_text: Текст сообщения
|
Returns:
|
||||||
first_name: Имя автора поста
|
Путь к сохраненному файлу, если файл был скачан успешно, иначе None
|
||||||
username: Юзернейм автора поста (может быть None)
|
"""
|
||||||
|
try:
|
||||||
Returns:
|
os.makedirs("files", exist_ok=True)
|
||||||
str: - Сформированный текст сообщения.
|
os.makedirs("files/photos", exist_ok=True)
|
||||||
"""
|
os.makedirs("files/videos", exist_ok=True)
|
||||||
# Экранируем post_text для безопасного использования в HTML
|
os.makedirs("files/music", exist_ok=True)
|
||||||
safe_post_text = html.escape(str(post_text)) if post_text else ""
|
os.makedirs("files/voice", exist_ok=True)
|
||||||
|
os.makedirs("files/video_notes", exist_ok=True)
|
||||||
# Экранируем username для безопасного использования в HTML
|
file = await message.bot.get_file(file_id)
|
||||||
safe_username = html.escape(username) if username else None
|
file_path = os.path.join("files", file.file_path)
|
||||||
|
await message.bot.download_file(file_path=file.file_path, destination=file_path)
|
||||||
# Формируем строку с информацией об авторе
|
return file_path
|
||||||
if safe_username:
|
except Exception as e:
|
||||||
author_info = f"{first_name} @{safe_username}"
|
logger.error(f"Ошибка скачивания фотографии: {e}")
|
||||||
else:
|
return None
|
||||||
author_info = f"{first_name} (Ник не указан)"
|
|
||||||
|
|
||||||
if "неанон" in post_text or "не анон" in post_text:
|
async def prepare_media_group_from_middlewares(album, post_caption: str = ''):
|
||||||
return f'Пост из ТГ:\n{safe_post_text}\n\nАвтор поста: {author_info}'
|
"""
|
||||||
elif "анон" in post_text:
|
Создает MediaGroup.
|
||||||
return f'Пост из ТГ:\n{safe_post_text}\n\nПост опубликован анонимно'
|
|
||||||
else:
|
Args:
|
||||||
return f'Пост из ТГ:\n{safe_post_text}\n\nАвтор поста: {author_info}'
|
album: Album объект из Telegram API.
|
||||||
|
post_caption: Текст подписи к первому фото.
|
||||||
|
|
||||||
async def download_file(message: types.Message, file_id: str):
|
Returns:
|
||||||
"""
|
Список InputMediaPhoto (MediaGroup).
|
||||||
Скачивает файл по file_id из Telegram.
|
"""
|
||||||
|
media_group = []
|
||||||
Args:
|
|
||||||
message: сообщение
|
for i, message in enumerate(album):
|
||||||
file_id: File ID фотографии
|
if message.photo:
|
||||||
filename: Имя файла, под которым будет сохранено фото
|
file_id = message.photo[-1].file_id
|
||||||
|
media_type = 'photo'
|
||||||
Returns:
|
elif message.video:
|
||||||
Путь к сохраненному файлу, если файл был скачан успешно, иначе None
|
file_id = message.video.file_id
|
||||||
"""
|
media_type = 'video'
|
||||||
try:
|
elif message.audio:
|
||||||
os.makedirs("files", exist_ok=True)
|
file_id = message.audio.file_id
|
||||||
os.makedirs("files/photos", exist_ok=True)
|
media_type = 'audio'
|
||||||
os.makedirs("files/videos", exist_ok=True)
|
else:
|
||||||
os.makedirs("files/music", exist_ok=True)
|
# Если нет фото, видео или аудио, пропускаем сообщение
|
||||||
os.makedirs("files/voice", exist_ok=True)
|
continue
|
||||||
os.makedirs("files/video_notes", exist_ok=True)
|
|
||||||
file = await message.bot.get_file(file_id)
|
# Формируем объект MediaGroup с учетом типа медиа
|
||||||
file_path = os.path.join("files", file.file_path)
|
if i == len(album) - 1:
|
||||||
await message.bot.download_file(file_path=file.file_path, destination=file_path)
|
if media_type == 'photo':
|
||||||
return file_path
|
media_group.append(InputMediaPhoto(media=file_id, caption=post_caption))
|
||||||
except Exception as e:
|
elif media_type == 'video':
|
||||||
logger.error(f"Ошибка скачивания фотографии: {e}")
|
media_group.append(InputMediaVideo(media=file_id, caption=post_caption))
|
||||||
return None
|
elif media_type == 'audio':
|
||||||
|
media_group.append(InputMediaAudio(media=file_id, caption=post_caption))
|
||||||
|
else:
|
||||||
async def prepare_media_group_from_middlewares(album, post_caption: str = ''):
|
if media_type == 'photo':
|
||||||
"""
|
media_group.append(InputMediaPhoto(media=file_id))
|
||||||
Создает MediaGroup.
|
elif media_type == 'video':
|
||||||
|
media_group.append(InputMediaVideo(media=file_id))
|
||||||
Args:
|
elif media_type == 'audio':
|
||||||
album: Album объект из Telegram API.
|
media_group.append(InputMediaAudio(media=file_id))
|
||||||
post_caption: Текст подписи к первому фото.
|
|
||||||
|
return media_group # Возвращаем MediaGroup
|
||||||
Returns:
|
|
||||||
Список InputMediaPhoto (MediaGroup).
|
|
||||||
"""
|
async def add_in_db_media_mediagroup(sent_message):
|
||||||
# Экранируем post_caption для безопасного использования в HTML
|
"""
|
||||||
safe_post_caption = html.escape(str(post_caption)) if post_caption else ""
|
Идентификатор медиа-группы
|
||||||
|
|
||||||
media_group = []
|
Args:
|
||||||
|
sent_message: sent_message объект из Telegram API
|
||||||
for i, message in enumerate(album):
|
|
||||||
if message.photo:
|
Returns:
|
||||||
file_id = message.photo[-1].file_id
|
Список InputFile (FSInputFile).
|
||||||
media_type = 'photo'
|
"""
|
||||||
elif message.video:
|
media_group_message_id = sent_message[-1].message_id # Получаем идентификатор медиа-группы
|
||||||
file_id = message.video.file_id
|
for i, message in enumerate(sent_message):
|
||||||
media_type = 'video'
|
if message.photo:
|
||||||
elif message.audio:
|
file_id = message.photo[-1].file_id
|
||||||
file_id = message.audio.file_id
|
file_path = await download_file(message, file_id=file_id)
|
||||||
media_type = 'audio'
|
BotDB.add_post_content_in_db(media_group_message_id, message.message_id, file_path, 'photo')
|
||||||
else:
|
elif message.video:
|
||||||
# Если нет фото, видео или аудио, пропускаем сообщение
|
file_id = message.video.file_id
|
||||||
continue
|
file_path = await download_file(message, file_id=file_id)
|
||||||
|
BotDB.add_post_content_in_db(media_group_message_id, message.message_id, file_path, 'video')
|
||||||
# Формируем объект MediaGroup с учетом типа медиа
|
else:
|
||||||
if i == len(album) - 1:
|
# Если нет фото, видео или аудио, или другой контент, пропускаем сообщение
|
||||||
if media_type == 'photo':
|
continue
|
||||||
media_group.append(InputMediaPhoto(media=file_id, caption=safe_post_caption))
|
|
||||||
elif media_type == 'video':
|
|
||||||
media_group.append(InputMediaVideo(media=file_id, caption=safe_post_caption))
|
async def add_in_db_media(sent_message):
|
||||||
elif media_type == 'audio':
|
"""
|
||||||
media_group.append(InputMediaAudio(media=file_id, caption=safe_post_caption))
|
Args:
|
||||||
else:
|
sent_message: sent_message объект из Telegram API
|
||||||
if media_type == 'photo':
|
|
||||||
media_group.append(InputMediaPhoto(media=file_id))
|
Returns:
|
||||||
elif media_type == 'video':
|
Список InputFile (FSInputFile).
|
||||||
media_group.append(InputMediaVideo(media=file_id))
|
"""
|
||||||
elif media_type == 'audio':
|
if sent_message.photo:
|
||||||
media_group.append(InputMediaAudio(media=file_id))
|
file_id = sent_message.photo[-1].file_id
|
||||||
|
file_path = await download_file(sent_message, file_id=file_id)
|
||||||
return media_group # Возвращаем MediaGroup
|
BotDB.add_post_content_in_db(sent_message.message_id, sent_message.message_id, file_path, 'photo')
|
||||||
|
elif sent_message.video:
|
||||||
|
file_id = sent_message.video.file_id
|
||||||
async def add_in_db_media_mediagroup(sent_message, bot_db):
|
file_path = await download_file(sent_message, file_id=file_id)
|
||||||
"""
|
BotDB.add_post_content_in_db(sent_message.message_id, sent_message.message_id, file_path, 'video')
|
||||||
Идентификатор медиа-группы
|
elif sent_message.voice:
|
||||||
|
file_id = sent_message.voice.file_id
|
||||||
Args:
|
file_path = await download_file(sent_message, file_id=file_id)
|
||||||
sent_message: sent_message объект из Telegram API
|
BotDB.add_post_content_in_db(sent_message.message_id, sent_message.message_id, file_path, 'voice')
|
||||||
bot_db: Экземпляр базы данных
|
elif sent_message.audio:
|
||||||
|
file_id = sent_message.audio.file_id
|
||||||
Returns:
|
file_path = await download_file(sent_message, file_id=file_id)
|
||||||
Список InputFile (FSInputFile).
|
BotDB.add_post_content_in_db(sent_message.message_id, sent_message.message_id, file_path, 'audio')
|
||||||
"""
|
elif sent_message.video_note:
|
||||||
media_group_message_id = sent_message[-1].message_id # Получаем идентификатор медиа-группы
|
file_id = sent_message.video_note.file_id
|
||||||
for i, message in enumerate(sent_message):
|
file_path = await download_file(sent_message, file_id=file_id)
|
||||||
if message.photo:
|
BotDB.add_post_content_in_db(sent_message.message_id, sent_message.message_id, file_path, 'video_note')
|
||||||
file_id = message.photo[-1].file_id
|
|
||||||
file_path = await download_file(message, file_id=file_id)
|
|
||||||
bot_db.add_post_content_in_db(media_group_message_id, message.message_id, file_path, 'photo')
|
async def send_media_group_message_to_private_chat(chat_id: int, message: types.Message,
|
||||||
elif message.video:
|
media_group: list[InputMediaPhoto]):
|
||||||
file_id = message.video.file_id
|
sent_message = await message.bot.send_media_group(
|
||||||
file_path = await download_file(message, file_id=file_id)
|
chat_id=chat_id,
|
||||||
bot_db.add_post_content_in_db(media_group_message_id, message.message_id, file_path, 'video')
|
media=media_group,
|
||||||
else:
|
)
|
||||||
# Если нет фото, видео или аудио, или другой контент, пропускаем сообщение
|
BotDB.add_post_in_db(sent_message[-1].message_id, sent_message[-1].caption, message.from_user.id)
|
||||||
continue
|
await add_in_db_media_mediagroup(sent_message)
|
||||||
|
message_id = sent_message[-1].message_id
|
||||||
|
return message_id
|
||||||
async def add_in_db_media(sent_message, bot_db):
|
|
||||||
"""
|
|
||||||
Args:
|
async def send_media_group_to_channel(bot, chat_id: int, post_content: list[tuple[str]], post_text: str):
|
||||||
sent_message: sent_message объект из Telegram API
|
"""
|
||||||
bot_db: Экземпляр базы данных
|
Отправляет медиа-группу с подписью к последнему файлу.
|
||||||
|
|
||||||
Returns:
|
Args:
|
||||||
Список InputFile (FSInputFile).
|
bot: Экземпляр бота aiogram.
|
||||||
"""
|
chat_id: ID чата для отправки.
|
||||||
if sent_message.photo:
|
post_content: Список кортежей с путями к файлам.
|
||||||
file_id = sent_message.photo[-1].file_id
|
post_text: Текст подписи.
|
||||||
file_path = await download_file(sent_message, file_id=file_id)
|
"""
|
||||||
bot_db.add_post_content_in_db(sent_message.message_id, sent_message.message_id, file_path, 'photo')
|
media = []
|
||||||
elif sent_message.video:
|
for file_path in post_content:
|
||||||
file_id = sent_message.video.file_id
|
try:
|
||||||
file_path = await download_file(sent_message, file_id=file_id)
|
file = FSInputFile(path=file_path[0])
|
||||||
bot_db.add_post_content_in_db(sent_message.message_id, sent_message.message_id, file_path, 'video')
|
type = file_path[1]
|
||||||
elif sent_message.voice:
|
if type == 'video':
|
||||||
file_id = sent_message.voice.file_id
|
media.append(types.InputMediaVideo(media=file))
|
||||||
file_path = await download_file(sent_message, file_id=file_id)
|
if type == 'photo':
|
||||||
bot_db.add_post_content_in_db(sent_message.message_id, sent_message.message_id, file_path, 'voice')
|
media.append(types.InputMediaPhoto(media=file))
|
||||||
elif sent_message.audio:
|
except FileNotFoundError:
|
||||||
file_id = sent_message.audio.file_id
|
logger.error(f"Файл не найден: {file_path[0]}")
|
||||||
file_path = await download_file(sent_message, file_id=file_id)
|
return
|
||||||
bot_db.add_post_content_in_db(sent_message.message_id, sent_message.message_id, file_path, 'audio')
|
|
||||||
elif sent_message.video_note:
|
# Добавляем подпись к последнему файлу
|
||||||
file_id = sent_message.video_note.file_id
|
if media:
|
||||||
file_path = await download_file(sent_message, file_id=file_id)
|
media[-1].caption = post_text
|
||||||
bot_db.add_post_content_in_db(sent_message.message_id, sent_message.message_id, file_path, 'video_note')
|
|
||||||
|
await bot.send_media_group(chat_id=chat_id, media=media)
|
||||||
|
|
||||||
async def send_media_group_message_to_private_chat(chat_id: int, message: types.Message,
|
|
||||||
media_group: list[InputMediaPhoto], bot_db):
|
async def send_text_message(chat_id, message: types.Message, post_text: str, markup: types.ReplyKeyboardMarkup = None):
|
||||||
sent_message = await message.bot.send_media_group(
|
if markup is None:
|
||||||
chat_id=chat_id,
|
sent_message = await message.bot.send_message(
|
||||||
media=media_group,
|
chat_id=chat_id,
|
||||||
)
|
text=post_text
|
||||||
bot_db.add_post_in_db(sent_message[-1].message_id, sent_message[-1].caption, message.from_user.id)
|
)
|
||||||
await add_in_db_media_mediagroup(sent_message, bot_db)
|
message_id = sent_message.message_id
|
||||||
message_id = sent_message[-1].message_id
|
return message_id
|
||||||
return message_id
|
else:
|
||||||
|
sent_message = await message.bot.send_message(
|
||||||
|
chat_id=chat_id,
|
||||||
async def send_media_group_to_channel(bot, chat_id: int, post_content: list[tuple[str]], post_text: str):
|
text=post_text,
|
||||||
"""
|
reply_markup=markup
|
||||||
Отправляет медиа-группу с подписью к последнему файлу.
|
)
|
||||||
|
message_id = sent_message.message_id
|
||||||
Args:
|
return message_id
|
||||||
bot: Экземпляр бота aiogram.
|
|
||||||
chat_id: ID чата для отправки.
|
|
||||||
post_content: Список кортежей с путями к файлам.
|
async def send_photo_message(chat_id, message: types.Message, photo: str, post_text: str,
|
||||||
post_text: Текст подписи.
|
markup: types.ReplyKeyboardMarkup = None):
|
||||||
"""
|
if markup is None:
|
||||||
media = []
|
sent_message = await message.bot.send_photo(
|
||||||
for file_path in post_content:
|
chat_id=chat_id,
|
||||||
try:
|
caption=post_text,
|
||||||
file = FSInputFile(path=file_path[0])
|
photo=photo
|
||||||
type = file_path[1]
|
)
|
||||||
if type == 'video':
|
else:
|
||||||
media.append(types.InputMediaVideo(media=file))
|
sent_message = await message.bot.send_photo(
|
||||||
if type == 'photo':
|
chat_id=chat_id,
|
||||||
media.append(types.InputMediaPhoto(media=file))
|
caption=post_text,
|
||||||
except FileNotFoundError:
|
photo=photo,
|
||||||
logger.error(f"Файл не найден: {file_path[0]}")
|
reply_markup=markup
|
||||||
return
|
)
|
||||||
|
return sent_message
|
||||||
# Добавляем подпись к последнему файлу
|
|
||||||
if media:
|
|
||||||
# Экранируем post_text для безопасного использования в HTML
|
async def send_video_message(chat_id, message: types.Message, video: str, post_text: str = "",
|
||||||
safe_post_text = html.escape(str(post_text)) if post_text else ""
|
markup: types.ReplyKeyboardMarkup = None):
|
||||||
media[-1].caption = safe_post_text
|
if markup is None:
|
||||||
|
sent_message = await message.bot.send_video(
|
||||||
await bot.send_media_group(chat_id=chat_id, media=media)
|
chat_id=chat_id,
|
||||||
|
caption=post_text,
|
||||||
|
video=video
|
||||||
async def send_text_message(chat_id, message: types.Message, post_text: str, markup: types.ReplyKeyboardMarkup = None):
|
)
|
||||||
# Экранируем post_text для безопасного использования в HTML
|
else:
|
||||||
safe_post_text = html.escape(str(post_text)) if post_text else ""
|
sent_message = await message.bot.send_video(
|
||||||
|
chat_id=chat_id,
|
||||||
if markup is None:
|
caption=post_text,
|
||||||
sent_message = await message.bot.send_message(
|
video=video,
|
||||||
chat_id=chat_id,
|
reply_markup=markup
|
||||||
text=safe_post_text
|
)
|
||||||
)
|
return sent_message
|
||||||
message_id = sent_message.message_id
|
|
||||||
return message_id
|
|
||||||
else:
|
async def send_video_note_message(chat_id, message: types.Message, video_note: str,
|
||||||
sent_message = await message.bot.send_message(
|
markup: types.ReplyKeyboardMarkup = None):
|
||||||
chat_id=chat_id,
|
if markup is None:
|
||||||
text=safe_post_text,
|
sent_message = await message.bot.send_video_note(
|
||||||
reply_markup=markup
|
chat_id=chat_id,
|
||||||
)
|
video_note=video_note
|
||||||
message_id = sent_message.message_id
|
)
|
||||||
return message_id
|
else:
|
||||||
|
sent_message = await message.bot.send_video_note(
|
||||||
|
chat_id=chat_id,
|
||||||
async def send_photo_message(chat_id, message: types.Message, photo: str, post_text: str,
|
video_note=video_note,
|
||||||
markup: types.ReplyKeyboardMarkup = None):
|
reply_markup=markup
|
||||||
# Экранируем post_text для безопасного использования в HTML
|
)
|
||||||
safe_post_text = html.escape(str(post_text)) if post_text else ""
|
return sent_message
|
||||||
|
|
||||||
if markup is None:
|
|
||||||
sent_message = await message.bot.send_photo(
|
async def send_audio_message(chat_id, message: types.Message, audio: str, post_text: str,
|
||||||
chat_id=chat_id,
|
markup: types.ReplyKeyboardMarkup = None):
|
||||||
caption=safe_post_text,
|
if markup is None:
|
||||||
photo=photo
|
sent_message = await message.bot.send_audio(
|
||||||
)
|
chat_id=chat_id,
|
||||||
else:
|
caption=post_text,
|
||||||
sent_message = await message.bot.send_photo(
|
audio=audio
|
||||||
chat_id=chat_id,
|
)
|
||||||
caption=safe_post_text,
|
else:
|
||||||
photo=photo,
|
sent_message = await message.bot.send_audio(
|
||||||
reply_markup=markup
|
chat_id=chat_id,
|
||||||
)
|
caption=post_text,
|
||||||
return sent_message
|
audio=audio,
|
||||||
|
reply_markup=markup
|
||||||
|
)
|
||||||
async def send_video_message(chat_id, message: types.Message, video: str, post_text: str = "",
|
return sent_message
|
||||||
markup: types.ReplyKeyboardMarkup = None):
|
|
||||||
# Экранируем post_text для безопасного использования в HTML
|
|
||||||
safe_post_text = html.escape(str(post_text)) if post_text else ""
|
async def send_voice_message(chat_id, message: types.Message, voice: str,
|
||||||
|
markup: types.ReplyKeyboardMarkup = None):
|
||||||
if markup is None:
|
if markup is None:
|
||||||
sent_message = await message.bot.send_video(
|
sent_message = await message.bot.send_voice(
|
||||||
chat_id=chat_id,
|
chat_id=chat_id,
|
||||||
caption=safe_post_text,
|
voice=voice
|
||||||
video=video
|
)
|
||||||
)
|
else:
|
||||||
else:
|
sent_message = await message.bot.send_voice(
|
||||||
sent_message = await message.bot.send_video(
|
chat_id=chat_id,
|
||||||
chat_id=chat_id,
|
voice=voice,
|
||||||
caption=safe_post_text,
|
reply_markup=markup
|
||||||
video=video,
|
)
|
||||||
reply_markup=markup
|
return sent_message
|
||||||
)
|
|
||||||
return sent_message
|
|
||||||
|
def check_access(user_id: int):
|
||||||
|
"""Проверка прав на совершение действий"""
|
||||||
async def send_video_note_message(chat_id, message: types.Message, video_note: str,
|
return BotDB.is_admin(user_id)
|
||||||
markup: types.ReplyKeyboardMarkup = None):
|
|
||||||
if markup is None:
|
|
||||||
sent_message = await message.bot.send_video_note(
|
def add_days_to_date(days: int):
|
||||||
chat_id=chat_id,
|
"""Прибавляет указанное количество дней к текущей дате и возвращает дату в формате DD-MM-YYYY."""
|
||||||
video_note=video_note
|
current_date = datetime.now()
|
||||||
)
|
future_date = current_date + timedelta(days=days)
|
||||||
else:
|
formatted_date = future_date.strftime("%d-%m-%Y")
|
||||||
sent_message = await message.bot.send_video_note(
|
return formatted_date
|
||||||
chat_id=chat_id,
|
|
||||||
video_note=video_note,
|
|
||||||
reply_markup=markup
|
def get_banned_users_list(offset: int):
|
||||||
)
|
"""
|
||||||
return sent_message
|
Возвращает сообщение со списком пользователей и словарь с ником + идентификатором
|
||||||
|
|
||||||
|
Args:
|
||||||
async def send_audio_message(chat_id, message: types.Message, audio: str, post_text: str,
|
offset: отступ для запроса в базу данных
|
||||||
markup: types.ReplyKeyboardMarkup = None):
|
|
||||||
# Экранируем post_text для безопасного использования в HTML
|
Returns:
|
||||||
safe_post_text = html.escape(str(post_text)) if post_text else ""
|
message - текст сообщения
|
||||||
|
user_ids - лист кортежей [(user_name: user_id)]
|
||||||
if markup is None:
|
"""
|
||||||
sent_message = await message.bot.send_audio(
|
users = BotDB.get_banned_users_from_db_with_limits(limit=7, offset=offset)
|
||||||
chat_id=chat_id,
|
message = "Список заблокированных пользователей:\n"
|
||||||
caption=safe_post_text,
|
|
||||||
audio=audio
|
for user in users:
|
||||||
)
|
message += f"Пользователь: {user[0]}\n"
|
||||||
else:
|
message += f"Причина бана: {user[2]}\n"
|
||||||
sent_message = await message.bot.send_audio(
|
message += f"Дата разбана: {user[3]}\n\n"
|
||||||
chat_id=chat_id,
|
return message
|
||||||
caption=safe_post_text,
|
|
||||||
audio=audio,
|
|
||||||
reply_markup=markup
|
def get_banned_users_buttons():
|
||||||
)
|
"""
|
||||||
return sent_message
|
Возвращает сообщение со списком пользователей и словарь с ником + идентификатором
|
||||||
|
|
||||||
|
Args:
|
||||||
async def send_voice_message(chat_id, message: types.Message, voice: str,
|
offset: отступ для запроса в базу данных
|
||||||
markup: types.ReplyKeyboardMarkup = None):
|
|
||||||
if markup is None:
|
Returns:
|
||||||
sent_message = await message.bot.send_voice(
|
message - текст сообщения
|
||||||
chat_id=chat_id,
|
user_ids - лист кортежей [(user_name: user_id)]
|
||||||
voice=voice
|
"""
|
||||||
)
|
users = BotDB.get_banned_users_from_db()
|
||||||
else:
|
user_ids = []
|
||||||
sent_message = await message.bot.send_voice(
|
|
||||||
chat_id=chat_id,
|
for user in users:
|
||||||
voice=voice,
|
user_ids.append((user[0], user[1]))
|
||||||
reply_markup=markup
|
return user_ids
|
||||||
)
|
|
||||||
return sent_message
|
|
||||||
|
def delete_user_blacklist(user_id: int):
|
||||||
|
return BotDB.delete_user_blacklist(user_id=user_id)
|
||||||
def check_access(user_id: int, bot_db):
|
|
||||||
"""Проверка прав на совершение действий"""
|
|
||||||
return bot_db.is_admin(user_id)
|
def check_username_and_full_name(user_id: int, username: str, full_name: str):
|
||||||
|
username_db, full_name_db = BotDB.get_username_and_full_name(user_id=user_id)
|
||||||
|
return username != username_db or full_name != full_name_db
|
||||||
def add_days_to_date(days: int):
|
|
||||||
"""Прибавляет указанное количество дней к текущей дате и возвращает дату в формате DD-MM-YYYY."""
|
|
||||||
current_date = datetime.now()
|
def unban_notifier(self):
|
||||||
future_date = current_date + timedelta(days=days)
|
# Получение сегодняшней даты в формате DD-MM-YYYY
|
||||||
formatted_date = future_date.strftime("%d-%m-%Y")
|
current_date = datetime.now()
|
||||||
return formatted_date
|
today = current_date.strftime("%d-%m-%Y")
|
||||||
|
# Получение списка разблокированных пользователей
|
||||||
|
unblocked_users = self.BotDB.get_users_for_unblock_today(today)
|
||||||
def get_banned_users_list(offset: int, bot_db):
|
message = "Разблокированные пользователи:\n"
|
||||||
"""
|
for user_id, user_name in unblocked_users.items():
|
||||||
Возвращает сообщение со списком пользователей и словарь с ником + идентификатором
|
message += f"ID: {user_id}, Имя: {user_name}\n"
|
||||||
|
|
||||||
Args:
|
# Отправка сообщения в канал
|
||||||
offset: отступ для запроса в базу данных
|
self.bot.send_message(self.GROUP_FOR_MESSAGE, message)
|
||||||
bot_db: Экземпляр базы данных
|
|
||||||
|
|
||||||
Returns:
|
async def update_user_info(source: str, message: types.Message):
|
||||||
message - текст сообщения
|
# Собираем данные
|
||||||
user_ids - лист кортежей [(user_name: user_id)]
|
full_name = message.from_user.full_name
|
||||||
"""
|
username = message.from_user.username
|
||||||
users = bot_db.get_banned_users_from_db_with_limits(limit=7, offset=offset)
|
first_name = get_first_name(message)
|
||||||
message = "Список заблокированных пользователей:\n"
|
is_bot = message.from_user.is_bot
|
||||||
|
language_code = message.from_user.language_code
|
||||||
for user in users:
|
user_id = message.from_user.id
|
||||||
# Экранируем пользовательские данные для безопасного использования
|
current_date = datetime.now()
|
||||||
safe_user_name = html.escape(str(user[0])) if user[0] else "Неизвестный пользователь"
|
date = current_date.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
safe_ban_reason = html.escape(str(user[2])) if user[2] else "Причина не указана"
|
# Выбираем эмодзю, пробегаемся циклом и смотрим что в базе такого еще не было
|
||||||
safe_unban_date = html.escape(str(user[3])) if user[3] else "Дата не указана"
|
user_emoji = get_random_emoji()
|
||||||
|
|
||||||
message += f"Пользователь: {safe_user_name}\n"
|
if not BotDB.user_exists(user_id):
|
||||||
message += f"Причина бана: {safe_ban_reason}\n"
|
BotDB.add_new_user_in_db(user_id, first_name, full_name, username, is_bot, language_code, user_emoji, date,
|
||||||
message += f"Дата разбана: {safe_unban_date}\n\n"
|
date)
|
||||||
return message
|
else:
|
||||||
|
is_need_update = check_username_and_full_name(user_id, username, full_name)
|
||||||
|
if is_need_update:
|
||||||
def get_banned_users_buttons(bot_db):
|
BotDB.update_username_and_full_name(user_id, username, full_name)
|
||||||
"""
|
if source != 'voice':
|
||||||
Возвращает сообщение со списком пользователей и словарь с ником + идентификатором
|
await message.answer(
|
||||||
|
f"Давно не виделись! Вижу что ты изменился;) Теперь буду звать тебя: {full_name}")
|
||||||
Args:
|
await message.bot.send_message(chat_id=GROUP_FOR_LOGS,
|
||||||
bot_db: Экземпляр базы данных
|
text=f'Для пользователя: {user_id} обновлены данные в БД.\nНовое имя: {full_name}\nНовый ник:{username}. Новый эмодзи:{user_emoji}')
|
||||||
|
sleep(1)
|
||||||
Returns:
|
BotDB.update_date_for_user(date, user_id)
|
||||||
message - текст сообщения
|
|
||||||
user_ids - лист кортежей [(user_name: user_id)]
|
|
||||||
"""
|
def check_user_emoji(message: types.Message):
|
||||||
users = bot_db.get_banned_users_from_db()
|
user_id = message.from_user.id
|
||||||
user_ids = []
|
user_emoji = BotDB.check_emoji_for_user(user_id=user_id)
|
||||||
|
if user_emoji is None:
|
||||||
for user in users:
|
user_emoji = get_random_emoji()
|
||||||
# Экранируем user_name для безопасного использования
|
BotDB.update_emoji_for_user(user_id=user_id, emoji=user_emoji)
|
||||||
safe_user_name = html.escape(str(user[0])) if user[0] else "Неизвестный пользователь"
|
return user_emoji
|
||||||
user_ids.append((safe_user_name, user[1]))
|
|
||||||
return user_ids
|
|
||||||
|
def get_random_emoji():
|
||||||
|
attempts = 0
|
||||||
def delete_user_blacklist(user_id: int, bot_db):
|
while attempts < 100:
|
||||||
return bot_db.delete_user_blacklist(user_id=user_id)
|
user_emoji = random.choice(emoji_list)
|
||||||
|
if not BotDB.check_emoji(user_emoji):
|
||||||
|
return user_emoji
|
||||||
def check_username_and_full_name(user_id: int, username: str, full_name: str, bot_db):
|
attempts += 1
|
||||||
username_db, full_name_db = bot_db.get_username_and_full_name(user_id=user_id)
|
logger.error("Не удалось найти уникальный эмодзи после нескольких попыток.")
|
||||||
return username != username_db or full_name != full_name_db
|
return "Эмоджи не определен"
|
||||||
|
|
||||||
|
|
||||||
def unban_notifier(self):
|
|
||||||
# Получение сегодняшней даты в формате DD-MM-YYYY
|
|
||||||
current_date = datetime.now()
|
|
||||||
today = current_date.strftime("%d-%m-%Y")
|
|
||||||
# Получение списка разблокированных пользователей
|
|
||||||
unblocked_users = self.BotDB.get_users_for_unblock_today(today)
|
|
||||||
message = "Разблокированные пользователи:\n"
|
|
||||||
for user_id, user_name in unblocked_users.items():
|
|
||||||
# Экранируем user_name для безопасного использования
|
|
||||||
safe_user_name = html.escape(str(user_name)) if user_name else "Неизвестный пользователь"
|
|
||||||
message += f"ID: {user_id}, Имя: {safe_user_name}\n"
|
|
||||||
|
|
||||||
# Отправка сообщения в канал
|
|
||||||
self.bot.send_message(self.GROUP_FOR_MESSAGE, message)
|
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
import html
|
|
||||||
|
|
||||||
|
|
||||||
def get_message(username: str, type_message: str):
|
def get_message(username: str, type_message: str):
|
||||||
constants = {
|
constants = {
|
||||||
'HELLO_MESSAGE': "Привет, username!👋🏼&Меня зовут Виби, я бот канала 'Влюбленный Бийск'❤🤖"
|
'HELLO_MESSAGE': "Привет, username!👋🏼&Меня зовут Виби, я бот канала 'Влюбленный Бийск'❤🤖"
|
||||||
@@ -26,7 +23,7 @@ def get_message(username: str, type_message: str):
|
|||||||
"&Мы рассмотрим и ответим тебе в ближайшее время☺️❤️",
|
"&Мы рассмотрим и ответим тебе в ближайшее время☺️❤️",
|
||||||
"DEL_MESSAGE": "username, напиши свое обращение или предложение✍"
|
"DEL_MESSAGE": "username, напиши свое обращение или предложение✍"
|
||||||
"&Мы рассмотрим и ответим тебе в ближайшее время☺❤",
|
"&Мы рассмотрим и ответим тебе в ближайшее время☺❤",
|
||||||
"BYE_MESSAGE": "Если позднее захочешь предложить еще один пост или обратиться к админам с вопросом, то просто пришли в чат команду 👉 /start"
|
"BYE_MESSAGE": "Если позднее захочешь предложить еще один пост или обратиться к админам с вопросом, то просто пришли в чат команду 👉 /restart"
|
||||||
"&&И тебе пока!👋🏼❤️",
|
"&&И тебе пока!👋🏼❤️",
|
||||||
"USER_ERROR": "Увы, я не понимаю тебя😐💔 Выбери один из пунктов в нижнем меню, а затем пиши.",
|
"USER_ERROR": "Увы, я не понимаю тебя😐💔 Выбери один из пунктов в нижнем меню, а затем пиши.",
|
||||||
"QUESTION": "Сообщение успешно отправлено❤️ Ответим, как только сможем😉",
|
"QUESTION": "Сообщение успешно отправлено❤️ Ответим, как только сможем😉",
|
||||||
@@ -38,10 +35,5 @@ def get_message(username: str, type_message: str):
|
|||||||
"&Ты можешь анонимно пообщаться, поделиться чем-то важным, обратиться напрямую к жителям🤝 Также можешь выступить перед аудиторией (спеть песню, рассказать стихотворение, шутку)🎤"
|
"&Ты можешь анонимно пообщаться, поделиться чем-то важным, обратиться напрямую к жителям🤝 Также можешь выступить перед аудиторией (спеть песню, рассказать стихотворение, шутку)🎤"
|
||||||
"&❗️Но пожалуйста не оскорбляй никого, и будь вежлив."
|
"&❗️Но пожалуйста не оскорбляй никого, и будь вежлив."
|
||||||
}
|
}
|
||||||
if username is None:
|
|
||||||
# Поведение ожидаемое тестами: TypeError при username=None
|
|
||||||
raise TypeError("username is None")
|
|
||||||
message = constants[type_message]
|
message = constants[type_message]
|
||||||
# Экранируем потенциально проблемные символы для HTML
|
return message.replace('username', username).replace('&', '\n')
|
||||||
message = message.replace('username', html.escape(username)).replace('&', '\n')
|
|
||||||
return message
|
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ class StateUser(StatesGroup):
|
|||||||
CHAT = State()
|
CHAT = State()
|
||||||
PRE_CHAT = State()
|
PRE_CHAT = State()
|
||||||
PRE_BAN = State()
|
PRE_BAN = State()
|
||||||
PRE_BAN_ID = State()
|
|
||||||
PRE_BAN_FORWARD = State()
|
|
||||||
BAN_2 = State()
|
BAN_2 = State()
|
||||||
BAN_3 = State()
|
BAN_3 = State()
|
||||||
BAN_4 = State()
|
BAN_4 = State()
|
||||||
|
|||||||
@@ -19,6 +19,6 @@ filename = f'{current_dir}/helper_bot_{today}.log'
|
|||||||
logger.add(
|
logger.add(
|
||||||
filename,
|
filename,
|
||||||
rotation="00:00",
|
rotation="00:00",
|
||||||
retention="30 days",
|
retention="5 days",
|
||||||
format="{time:YYYY-MM-DD at HH:mm:ss} | {level} | {name} | {line} | {message}",
|
format="{time:YYYY-MM-DD at HH:mm:ss} | {level} | {name} | {line} | {message}",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,38 +1,34 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
from database.db import BotDB
|
||||||
# Добавляем путь к корневой директории проекта
|
|
||||||
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
|
# Получаем текущую директорию
|
||||||
|
current_dir = os.path.dirname(__file__)
|
||||||
from database.db import BotDB
|
|
||||||
|
# Переходим на уровень выше
|
||||||
# Получаем текущую директорию
|
parent_dir = os.path.dirname(current_dir)
|
||||||
current_dir = os.path.dirname(__file__)
|
|
||||||
|
BotDB = BotDB(parent_dir, 'database/tg-bot-database')
|
||||||
# Переходим на уровень выше
|
|
||||||
parent_dir = os.path.dirname(current_dir)
|
|
||||||
|
def get_filename():
|
||||||
BotDB = BotDB(parent_dir, 'tg-bot-database.db')
|
"""Возвращает имя файла без расширения."""
|
||||||
|
filename = os.path.basename(__file__)
|
||||||
|
filename = os.path.splitext(filename)[0]
|
||||||
def get_filename():
|
return filename
|
||||||
"""Возвращает имя файла без расширения."""
|
|
||||||
filename = os.path.basename(__file__)
|
|
||||||
filename = os.path.splitext(filename)[0]
|
def main():
|
||||||
return filename
|
migrations_init = """
|
||||||
|
CREATE TABLE IF NOT EXISTS migrations (
|
||||||
|
version INTEGER PRIMARY KEY NOT NULL,
|
||||||
def main():
|
script_name TEXT NOT NULL,
|
||||||
migrations_init = """
|
created_at TEXT
|
||||||
CREATE TABLE IF NOT EXISTS migrations (
|
);
|
||||||
version INTEGER PRIMARY KEY NOT NULL,
|
"""
|
||||||
script_name TEXT NOT NULL,
|
BotDB.create_table(migrations_init)
|
||||||
created_at TEXT
|
BotDB.update_version(0, get_filename())
|
||||||
);
|
|
||||||
"""
|
|
||||||
BotDB.create_table(migrations_init)
|
if __name__ == "__main__":
|
||||||
BotDB.update_version(0, get_filename())
|
main()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
|
|||||||
@@ -1,67 +1,63 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
from database.db import BotDB
|
||||||
# Добавляем путь к корневой директории проекта
|
|
||||||
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
|
# Получаем текущую директорию
|
||||||
|
current_dir = os.path.dirname(__file__)
|
||||||
from database.db import BotDB
|
|
||||||
|
# Переходим на уровень выше
|
||||||
# Получаем текущую директорию
|
parent_dir = os.path.dirname(current_dir)
|
||||||
current_dir = os.path.dirname(__file__)
|
|
||||||
|
BotDB = BotDB(parent_dir, 'database/tg-bot-database')
|
||||||
# Переходим на уровень выше
|
|
||||||
parent_dir = os.path.dirname(current_dir)
|
|
||||||
|
def get_filename():
|
||||||
BotDB = BotDB(parent_dir, 'tg-bot-database.db')
|
"""Возвращает имя файла без расширения."""
|
||||||
|
filename = os.path.basename(__file__)
|
||||||
|
filename = os.path.splitext(filename)[0]
|
||||||
def get_filename():
|
return filename
|
||||||
"""Возвращает имя файла без расширения."""
|
|
||||||
filename = os.path.basename(__file__)
|
|
||||||
filename = os.path.splitext(filename)[0]
|
def main():
|
||||||
return filename
|
# Проверка версии миграций
|
||||||
|
current_version = BotDB.get_current_version() # Добавьте функцию для получения версии
|
||||||
|
|
||||||
def main():
|
# Выполнение миграций и проверка последней версии
|
||||||
# Проверка версии миграций
|
if current_version < 1:
|
||||||
current_version = BotDB.get_current_version() # Добавьте функцию для получения версии
|
# Скрипты миграции
|
||||||
|
create_table_sql_1 = """
|
||||||
# Выполнение миграций и проверка последней версии
|
CREATE TABLE IF NOT EXISTS "admins" (
|
||||||
if current_version < 1:
|
user_id INTEGER NOT NULL,
|
||||||
# Скрипты миграции
|
"role" TEXT
|
||||||
create_table_sql_1 = """
|
);
|
||||||
CREATE TABLE IF NOT EXISTS "admins" (
|
"""
|
||||||
user_id INTEGER NOT NULL,
|
create_table_sql_2 = """CREATE TABLE IF NOT EXISTS "blacklist"
|
||||||
"role" TEXT
|
(
|
||||||
);
|
"user_id" INTEGER NOT NULL UNIQUE,
|
||||||
"""
|
"user_name" INTEGER,
|
||||||
create_table_sql_2 = """CREATE TABLE IF NOT EXISTS "blacklist"
|
"message_for_user" INTEGER,
|
||||||
(
|
"date_to_unban" INTEGER
|
||||||
"user_id" INTEGER NOT NULL UNIQUE,
|
);
|
||||||
"user_name" INTEGER,
|
"""
|
||||||
"message_for_user" INTEGER,
|
create_table_sql_3 = """
|
||||||
"date_to_unban" INTEGER
|
CREATE TABLE IF NOT EXISTS user_messages (
|
||||||
);
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
"""
|
message_text TEXT,
|
||||||
create_table_sql_3 = """
|
user_id INTEGER,
|
||||||
CREATE TABLE IF NOT EXISTS user_messages (
|
message_id INTEGER NOT NULL,
|
||||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
date TEXT
|
||||||
message_text TEXT,
|
);
|
||||||
user_id INTEGER,
|
"""
|
||||||
message_id INTEGER NOT NULL,
|
# Применение миграции
|
||||||
date TEXT
|
BotDB.create_table(create_table_sql_1)
|
||||||
);
|
BotDB.create_table(create_table_sql_2)
|
||||||
"""
|
BotDB.create_table(create_table_sql_3)
|
||||||
# Применение миграции
|
BotDB.add_admin(842766148, 'creator')
|
||||||
BotDB.create_table(create_table_sql_1)
|
BotDB.add_admin(920057022, 'admin')
|
||||||
BotDB.create_table(create_table_sql_2)
|
filename = get_filename()
|
||||||
BotDB.create_table(create_table_sql_3)
|
|
||||||
BotDB.add_admin(842766148, 'creator')
|
BotDB.update_version(1, filename)
|
||||||
BotDB.add_admin(920057022, 'admin')
|
|
||||||
filename = get_filename()
|
|
||||||
|
if __name__ == "__main__":
|
||||||
BotDB.update_version(1, filename)
|
main()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
|
|||||||
@@ -1,65 +1,61 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
from database.db import BotDB
|
||||||
# Добавляем путь к корневой директории проекта
|
|
||||||
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
|
# Получаем текущую директорию
|
||||||
|
current_dir = os.path.dirname(__file__)
|
||||||
from database.db import BotDB
|
|
||||||
|
# Переходим на уровень выше
|
||||||
# Получаем текущую директорию
|
parent_dir = os.path.dirname(current_dir)
|
||||||
current_dir = os.path.dirname(__file__)
|
|
||||||
|
BotDB = BotDB(parent_dir, 'database/tg-bot-database')
|
||||||
# Переходим на уровень выше
|
|
||||||
parent_dir = os.path.dirname(current_dir)
|
|
||||||
|
def get_filename():
|
||||||
BotDB = BotDB(parent_dir, 'tg-bot-database.db')
|
"""Возвращает имя файла без расширения."""
|
||||||
|
filename = os.path.basename(__file__)
|
||||||
|
filename = os.path.splitext(filename)[0]
|
||||||
def get_filename():
|
return filename
|
||||||
"""Возвращает имя файла без расширения."""
|
|
||||||
filename = os.path.basename(__file__)
|
|
||||||
filename = os.path.splitext(filename)[0]
|
def main():
|
||||||
return filename
|
# Проверка версии миграций
|
||||||
|
current_version = BotDB.get_current_version() # Добавьте функцию для получения версии
|
||||||
|
|
||||||
def main():
|
# Выполнение миграций и проверка последней версии
|
||||||
# Проверка версии миграций
|
if current_version < 2:
|
||||||
current_version = BotDB.get_current_version() # Добавьте функцию для получения версии
|
# Скрипты миграции
|
||||||
|
create_table_sql_1 = """
|
||||||
# Выполнение миграций и проверка последней версии
|
CREATE TABLE IF NOT EXISTS "post_from_telegram_suggest"
|
||||||
if current_version < 2:
|
(
|
||||||
# Скрипты миграции
|
message_id INTEGER not null,
|
||||||
create_table_sql_1 = """
|
text TEXT,
|
||||||
CREATE TABLE IF NOT EXISTS "post_from_telegram_suggest"
|
helper_text_message_id INTEGER,
|
||||||
(
|
author_id INTEGER,
|
||||||
message_id INTEGER not null,
|
created_at TEXT
|
||||||
text TEXT,
|
);
|
||||||
helper_text_message_id INTEGER,
|
"""
|
||||||
author_id INTEGER,
|
create_table_sql_2 = """
|
||||||
created_at TEXT
|
CREATE TABLE IF NOT EXISTS message_link_to_content (
|
||||||
);
|
post_id INTEGER NOT NULL,
|
||||||
"""
|
message_id INTEGER NOT NULL
|
||||||
create_table_sql_2 = """
|
);
|
||||||
CREATE TABLE IF NOT EXISTS message_link_to_content (
|
"""
|
||||||
post_id INTEGER NOT NULL,
|
create_table_sql_3 = """
|
||||||
message_id INTEGER NOT NULL
|
CREATE TABLE IF NOT EXISTS content_post_from_telegram (
|
||||||
);
|
message_id INTEGER NOT NULL,
|
||||||
"""
|
content_name TEXT NOT NULL,
|
||||||
create_table_sql_3 = """
|
content_type TEXT
|
||||||
CREATE TABLE IF NOT EXISTS content_post_from_telegram (
|
);
|
||||||
message_id INTEGER NOT NULL,
|
"""
|
||||||
content_name TEXT NOT NULL,
|
# Применение миграции
|
||||||
content_type TEXT
|
BotDB.create_table(create_table_sql_1)
|
||||||
);
|
BotDB.create_table(create_table_sql_2)
|
||||||
"""
|
BotDB.create_table(create_table_sql_3)
|
||||||
# Применение миграции
|
filename = get_filename()
|
||||||
BotDB.create_table(create_table_sql_1)
|
|
||||||
BotDB.create_table(create_table_sql_2)
|
BotDB.update_version(2, filename)
|
||||||
BotDB.create_table(create_table_sql_3)
|
|
||||||
filename = get_filename()
|
|
||||||
|
if __name__ == "__main__":
|
||||||
BotDB.update_version(2, filename)
|
main()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# Добавляем путь к корневой директории проекта
|
|
||||||
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
|
|
||||||
|
|
||||||
from database.db import BotDB
|
|
||||||
|
|
||||||
# Получаем текущую директорию
|
|
||||||
current_dir = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
# Переходим на уровень выше
|
|
||||||
parent_dir = os.path.dirname(current_dir)
|
|
||||||
|
|
||||||
BotDB = BotDB(parent_dir, 'tg-bot-database.db')
|
|
||||||
|
|
||||||
|
|
||||||
def get_filename():
|
|
||||||
"""Возвращает имя файла без расширения."""
|
|
||||||
filename = os.path.basename(__file__)
|
|
||||||
filename = os.path.splitext(filename)[0]
|
|
||||||
return filename
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
# Проверка версии миграций
|
|
||||||
current_version = BotDB.get_current_version()
|
|
||||||
|
|
||||||
# Выполнение миграций и проверка последней версии
|
|
||||||
if current_version < 3:
|
|
||||||
# Скрипт миграции для создания таблицы our_users
|
|
||||||
create_table_sql = """
|
|
||||||
CREATE TABLE IF NOT EXISTS "our_users" (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
user_id INTEGER NOT NULL UNIQUE,
|
|
||||||
first_name TEXT,
|
|
||||||
full_name TEXT,
|
|
||||||
username TEXT,
|
|
||||||
is_bot BOOLEAN DEFAULT 0,
|
|
||||||
language_code TEXT,
|
|
||||||
date_added TEXT,
|
|
||||||
date_changed TEXT,
|
|
||||||
has_stickers BOOLEAN DEFAULT 0
|
|
||||||
);
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Применение миграции
|
|
||||||
BotDB.create_table(create_table_sql)
|
|
||||||
filename = get_filename()
|
|
||||||
|
|
||||||
BotDB.update_version(3, filename)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
|
|
||||||
25
pytest.ini
25
pytest.ini
@@ -1,19 +1,8 @@
|
|||||||
[tool:pytest]
|
[pytest]
|
||||||
testpaths = tests
|
pythonpath = .
|
||||||
python_files = test_*.py
|
python_files = test_*.py *_test.py
|
||||||
python_classes = Test*
|
|
||||||
python_functions = test_*
|
python_functions = test_*
|
||||||
addopts =
|
testpaths = tests
|
||||||
-v
|
|
||||||
--tb=short
|
[report]
|
||||||
--strict-markers
|
omit = *myenv/*, custom_logger.py, *venv/*, tests/*
|
||||||
--disable-warnings
|
|
||||||
--asyncio-mode=auto
|
|
||||||
markers =
|
|
||||||
asyncio: marks tests as async (deselect with '-m "not asyncio"')
|
|
||||||
slow: marks tests as slow (deselect with '-m "not slow"')
|
|
||||||
integration: marks tests as integration tests
|
|
||||||
unit: marks tests as unit tests
|
|
||||||
filterwarnings =
|
|
||||||
ignore::DeprecationWarning
|
|
||||||
ignore::PendingDeprecationWarning
|
|
||||||
@@ -1,22 +1,23 @@
|
|||||||
APScheduler==3.10.4
|
APScheduler==3.10.4
|
||||||
certifi~=2024.6.2
|
certifi~=2024.6.2
|
||||||
charset-normalizer==3.3.2
|
charset-normalizer==3.3.2
|
||||||
coverage==7.5.4
|
coverage==7.5.4
|
||||||
exceptiongroup==1.2.1
|
exceptiongroup==1.2.1
|
||||||
idna==3.7
|
idna==3.7
|
||||||
iniconfig==2.0.0
|
iniconfig==2.0.0
|
||||||
loguru==0.7.2
|
loguru==0.7.2
|
||||||
packaging==24.1
|
packaging==24.1
|
||||||
pluggy==1.5.0
|
pluggy==1.5.0
|
||||||
pytest==8.2.2
|
pytest==8.2.2
|
||||||
pytz==2024.1
|
pytz==2024.1
|
||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
tomli==2.0.1
|
tomli==2.0.1
|
||||||
tzlocal==5.2
|
tzlocal==5.2
|
||||||
urllib3~=2.2.1
|
urllib3~=2.2.1
|
||||||
pip~=23.2.1
|
pip~=23.2.1
|
||||||
attrs~=23.2.0
|
attrs~=23.2.0
|
||||||
typing_extensions~=4.12.2
|
typing_extensions~=4.12.2
|
||||||
aiohttp~=3.9.5
|
aiohttp~=3.9.5
|
||||||
aiogram~=3.10.0
|
aiogram~=3.10.0
|
||||||
|
emoji~=2.14.0
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from helper_bot.main import start_bot
|
from helper_bot.main import start_bot
|
||||||
from helper_bot.utils.base_dependency_factory import get_global_instance
|
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
asyncio.run(start_bot(get_global_instance()))
|
asyncio.run(start_bot(BaseDependencyFactory()))
|
||||||
|
|||||||
@@ -1,234 +0,0 @@
|
|||||||
import pytest
|
|
||||||
import asyncio
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from unittest.mock import Mock, AsyncMock, patch
|
|
||||||
from aiogram.types import Message, User, Chat
|
|
||||||
from aiogram.fsm.context import FSMContext
|
|
||||||
|
|
||||||
from database.db import BotDB
|
|
||||||
|
|
||||||
# Импортируем моки в самом начале
|
|
||||||
import tests.mocks
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
def event_loop():
|
|
||||||
"""Создает event loop для асинхронных тестов"""
|
|
||||||
loop = asyncio.get_event_loop_policy().new_event_loop()
|
|
||||||
yield loop
|
|
||||||
loop.close()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_message():
|
|
||||||
"""Создает базовый мок сообщения для тестов"""
|
|
||||||
message = Mock(spec=Message)
|
|
||||||
message.from_user = Mock(spec=User)
|
|
||||||
message.from_user.id = 123456
|
|
||||||
message.from_user.full_name = "Test User"
|
|
||||||
message.from_user.username = "testuser"
|
|
||||||
message.from_user.first_name = "Test"
|
|
||||||
message.from_user.is_bot = False
|
|
||||||
message.from_user.language_code = "ru"
|
|
||||||
message.chat = Mock(spec=Chat)
|
|
||||||
message.chat.id = 123456
|
|
||||||
message.chat.type = "private"
|
|
||||||
message.message_id = 1
|
|
||||||
message.text = "/start"
|
|
||||||
message.forward = AsyncMock()
|
|
||||||
message.answer = AsyncMock()
|
|
||||||
message.answer_sticker = AsyncMock()
|
|
||||||
message.bot.send_message = AsyncMock()
|
|
||||||
return message
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_state():
|
|
||||||
"""Создает мок состояния FSM для тестов"""
|
|
||||||
state = Mock(spec=FSMContext)
|
|
||||||
state.set_state = AsyncMock()
|
|
||||||
state.get_state = AsyncMock(return_value="START")
|
|
||||||
return state
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_db():
|
|
||||||
"""Создает мок базы данных для тестов"""
|
|
||||||
db = Mock(spec=BotDB)
|
|
||||||
db.user_exists = Mock(return_value=False)
|
|
||||||
db.add_new_user_in_db = Mock()
|
|
||||||
db.update_date_for_user = Mock()
|
|
||||||
db.update_username_and_full_name = Mock()
|
|
||||||
db.add_post_in_db = Mock()
|
|
||||||
db.update_info_about_stickers = Mock()
|
|
||||||
db.add_new_message_in_db = Mock()
|
|
||||||
db.get_info_about_stickers = Mock(return_value=False)
|
|
||||||
db.get_username_and_full_name = Mock(return_value=("testuser", "Test User"))
|
|
||||||
return db
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_bot():
|
|
||||||
"""Создает мок бота для тестов"""
|
|
||||||
bot = AsyncMock()
|
|
||||||
bot.send_message = AsyncMock()
|
|
||||||
bot.delete_webhook = AsyncMock()
|
|
||||||
return bot
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_dispatcher():
|
|
||||||
"""Создает мок диспетчера для тестов"""
|
|
||||||
dispatcher = AsyncMock()
|
|
||||||
dispatcher.include_routers = Mock()
|
|
||||||
dispatcher.start_polling = AsyncMock()
|
|
||||||
return dispatcher
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def test_settings():
|
|
||||||
"""Возвращает тестовые настройки"""
|
|
||||||
return {
|
|
||||||
'Telegram': {
|
|
||||||
'bot_token': 'test_token_123',
|
|
||||||
'preview_link': False,
|
|
||||||
'group_for_posts': '-1001234567890',
|
|
||||||
'group_for_message': '-1001234567891',
|
|
||||||
'main_public': '-1001234567892',
|
|
||||||
'group_for_logs': '-1001234567893',
|
|
||||||
'important_logs': '-1001234567894'
|
|
||||||
},
|
|
||||||
'Settings': {
|
|
||||||
'logs': True,
|
|
||||||
'test': False
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_factory(test_settings, mock_db):
|
|
||||||
"""Создает мок фабрики зависимостей"""
|
|
||||||
factory = Mock()
|
|
||||||
factory.settings = test_settings
|
|
||||||
factory.get_db = Mock(return_value=mock_db)
|
|
||||||
factory.database = mock_db
|
|
||||||
return factory
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def sample_photo_message(mock_message):
|
|
||||||
"""Создает сообщение с фото для тестов"""
|
|
||||||
mock_message.content_type = 'photo'
|
|
||||||
mock_message.caption = 'Тестовое фото'
|
|
||||||
mock_message.media_group_id = None
|
|
||||||
mock_message.photo = [Mock()]
|
|
||||||
mock_message.photo[-1].file_id = 'photo_file_id'
|
|
||||||
return mock_message
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def sample_video_message(mock_message):
|
|
||||||
"""Создает сообщение с видео для тестов"""
|
|
||||||
mock_message.content_type = 'video'
|
|
||||||
mock_message.caption = 'Тестовое видео'
|
|
||||||
mock_message.media_group_id = None
|
|
||||||
mock_message.video = Mock()
|
|
||||||
mock_message.video.file_id = 'video_file_id'
|
|
||||||
return mock_message
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def sample_audio_message(mock_message):
|
|
||||||
"""Создает сообщение с аудио для тестов"""
|
|
||||||
mock_message.content_type = 'audio'
|
|
||||||
mock_message.caption = 'Тестовое аудио'
|
|
||||||
mock_message.media_group_id = None
|
|
||||||
mock_message.audio = Mock()
|
|
||||||
mock_message.audio.file_id = 'audio_file_id'
|
|
||||||
return mock_message
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def sample_voice_message(mock_message):
|
|
||||||
"""Создает голосовое сообщение для тестов"""
|
|
||||||
mock_message.content_type = 'voice'
|
|
||||||
mock_message.media_group_id = None
|
|
||||||
mock_message.voice = Mock()
|
|
||||||
mock_message.voice.file_id = 'voice_file_id'
|
|
||||||
return mock_message
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def sample_video_note_message(mock_message):
|
|
||||||
"""Создает видеокружок для тестов"""
|
|
||||||
mock_message.content_type = 'video_note'
|
|
||||||
mock_message.media_group_id = None
|
|
||||||
mock_message.video_note = Mock()
|
|
||||||
mock_message.video_note.file_id = 'video_note_file_id'
|
|
||||||
return mock_message
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def sample_media_group(mock_message):
|
|
||||||
"""Создает медиагруппу для тестов"""
|
|
||||||
mock_message.media_group_id = 'group_123'
|
|
||||||
mock_message.content_type = 'photo'
|
|
||||||
album = [mock_message]
|
|
||||||
album[0].caption = 'Подпись к медиагруппе'
|
|
||||||
return album
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def sample_text_message(mock_message):
|
|
||||||
"""Создает текстовое сообщение для тестов"""
|
|
||||||
mock_message.content_type = 'text'
|
|
||||||
mock_message.text = 'Тестовое текстовое сообщение'
|
|
||||||
mock_message.media_group_id = None
|
|
||||||
return mock_message
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def sample_document_message(mock_message):
|
|
||||||
"""Создает сообщение с документом для тестов"""
|
|
||||||
mock_message.content_type = 'document'
|
|
||||||
mock_message.media_group_id = None
|
|
||||||
return mock_message
|
|
||||||
|
|
||||||
|
|
||||||
# Маркеры для категоризации тестов
|
|
||||||
def pytest_configure(config):
|
|
||||||
"""Настройка маркеров pytest"""
|
|
||||||
config.addinivalue_line(
|
|
||||||
"markers", "asyncio: mark test as async"
|
|
||||||
)
|
|
||||||
config.addinivalue_line(
|
|
||||||
"markers", "slow: mark test as slow"
|
|
||||||
)
|
|
||||||
config.addinivalue_line(
|
|
||||||
"markers", "integration: mark test as integration test"
|
|
||||||
)
|
|
||||||
config.addinivalue_line(
|
|
||||||
"markers", "unit: mark test as unit test"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Автоматическая маркировка тестов
|
|
||||||
def pytest_collection_modifyitems(config, items):
|
|
||||||
"""Автоматически маркирует тесты по их расположению"""
|
|
||||||
for item in items:
|
|
||||||
# Маркируем асинхронные тесты
|
|
||||||
if "async" in item.name or "Async" in item.name:
|
|
||||||
item.add_marker(pytest.mark.asyncio)
|
|
||||||
|
|
||||||
# Маркируем интеграционные тесты
|
|
||||||
if "integration" in item.name.lower() or "Integration" in str(item.cls):
|
|
||||||
item.add_marker(pytest.mark.integration)
|
|
||||||
|
|
||||||
# Маркируем unit тесты
|
|
||||||
if "unit" in item.name.lower() or "Unit" in str(item.cls):
|
|
||||||
item.add_marker(pytest.mark.unit)
|
|
||||||
|
|
||||||
# Маркируем медленные тесты
|
|
||||||
if "slow" in item.name.lower() or "Slow" in str(item.cls):
|
|
||||||
item.add_marker(pytest.mark.slow)
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
"""
|
|
||||||
Моки для тестового окружения
|
|
||||||
"""
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
from unittest.mock import Mock, patch
|
|
||||||
|
|
||||||
# Патчим загрузку настроек до импорта модулей
|
|
||||||
def setup_test_mocks():
|
|
||||||
"""Настройка моков для тестов"""
|
|
||||||
# Мокаем ConfigParser
|
|
||||||
mock_config = Mock()
|
|
||||||
|
|
||||||
def mock_getitem(section):
|
|
||||||
if section == 'Telegram':
|
|
||||||
return {
|
|
||||||
'bot_token': 'test_token_123',
|
|
||||||
'preview_link': 'False',
|
|
||||||
'main_public': '@test',
|
|
||||||
'group_for_posts': '-1001234567890',
|
|
||||||
'group_for_message': '-1001234567891',
|
|
||||||
'group_for_logs': '-1001234567893',
|
|
||||||
'important_logs': '-1001234567894',
|
|
||||||
'test_channel': '-1001234567895'
|
|
||||||
}
|
|
||||||
elif section == 'Settings':
|
|
||||||
return {
|
|
||||||
'logs': 'True',
|
|
||||||
'test': 'False'
|
|
||||||
}
|
|
||||||
return {}
|
|
||||||
|
|
||||||
# Создаем MagicMock для поддержки __getitem__
|
|
||||||
mock_config_instance = Mock()
|
|
||||||
mock_config_instance.sections.return_value = ['Telegram', 'Settings']
|
|
||||||
mock_config_instance.__getitem__ = Mock(side_effect=mock_getitem)
|
|
||||||
|
|
||||||
mock_config.return_value = mock_config_instance
|
|
||||||
|
|
||||||
# Применяем патчи
|
|
||||||
config_patcher = patch('helper_bot.utils.base_dependency_factory.configparser.ConfigParser', mock_config)
|
|
||||||
config_patcher.start()
|
|
||||||
|
|
||||||
# Мокаем BotDB
|
|
||||||
mock_db = Mock()
|
|
||||||
db_patcher = patch('helper_bot.utils.base_dependency_factory.BotDB', mock_db)
|
|
||||||
db_patcher.start()
|
|
||||||
|
|
||||||
return config_patcher, db_patcher
|
|
||||||
|
|
||||||
# Настраиваем моки при импорте модуля
|
|
||||||
config_patcher, db_patcher = setup_test_mocks()
|
|
||||||
@@ -1,339 +0,0 @@
|
|||||||
# Импортируем моки в самом начале
|
|
||||||
import tests.mocks
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
import asyncio
|
|
||||||
from unittest.mock import Mock, AsyncMock, patch, MagicMock
|
|
||||||
from aiogram import Bot, Dispatcher
|
|
||||||
from aiogram.types import Message, User, Chat, MessageEntity
|
|
||||||
from aiogram.fsm.context import FSMContext
|
|
||||||
from aiogram.fsm.storage.memory import MemoryStorage
|
|
||||||
|
|
||||||
from helper_bot.main import start_bot
|
|
||||||
from helper_bot.handlers.private.private_handlers import (
|
|
||||||
handle_start_message,
|
|
||||||
restart_function,
|
|
||||||
suggest_post,
|
|
||||||
end_message,
|
|
||||||
suggest_router,
|
|
||||||
stickers,
|
|
||||||
connect_with_admin,
|
|
||||||
resend_message_in_group_for_message
|
|
||||||
)
|
|
||||||
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory, get_global_instance
|
|
||||||
from database.db import BotDB
|
|
||||||
|
|
||||||
|
|
||||||
class TestBotStartup:
|
|
||||||
"""Тесты для проверки запуска бота"""
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_bot_initialization(self):
|
|
||||||
"""Тест инициализации бота"""
|
|
||||||
with patch('helper_bot.main.Bot') as mock_bot_class:
|
|
||||||
with patch('helper_bot.main.Dispatcher') as mock_dp_class:
|
|
||||||
with patch('helper_bot.main.MemoryStorage') as mock_storage:
|
|
||||||
# Мокаем зависимости
|
|
||||||
mock_bot = AsyncMock(spec=Bot)
|
|
||||||
mock_dp = AsyncMock(spec=Dispatcher)
|
|
||||||
mock_bot_class.return_value = mock_bot
|
|
||||||
mock_dp_class.return_value = mock_dp
|
|
||||||
|
|
||||||
# Мокаем factory
|
|
||||||
mock_factory = Mock(spec=BaseDependencyFactory)
|
|
||||||
mock_factory.settings = {
|
|
||||||
'Telegram': {
|
|
||||||
'bot_token': 'test_token',
|
|
||||||
'preview_link': False
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Запускаем бота
|
|
||||||
await start_bot(mock_factory)
|
|
||||||
|
|
||||||
# Проверяем, что бот был создан с правильными параметрами
|
|
||||||
mock_bot_class.assert_called_once()
|
|
||||||
call_args = mock_bot_class.call_args
|
|
||||||
assert call_args[1]['token'] == 'test_token'
|
|
||||||
assert call_args[1]['default'].parse_mode == 'HTML'
|
|
||||||
assert call_args[1]['default'].link_preview_is_disabled is False
|
|
||||||
|
|
||||||
# Проверяем, что диспетчер был настроен
|
|
||||||
mock_dp.include_routers.assert_called_once()
|
|
||||||
mock_bot.delete_webhook.assert_called_once_with(drop_pending_updates=True)
|
|
||||||
mock_dp.start_polling.assert_called_once_with(mock_bot, skip_updates=True)
|
|
||||||
|
|
||||||
|
|
||||||
class TestPrivateHandlers:
|
|
||||||
"""Тесты для приватных хэндлеров"""
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_message(self):
|
|
||||||
"""Создает мок сообщения"""
|
|
||||||
message = Mock(spec=Message)
|
|
||||||
message.from_user = Mock(spec=User)
|
|
||||||
message.from_user.id = 123456
|
|
||||||
message.from_user.full_name = "Test User"
|
|
||||||
message.from_user.username = "testuser"
|
|
||||||
message.from_user.first_name = "Test"
|
|
||||||
message.from_user.is_bot = False
|
|
||||||
message.from_user.language_code = "ru"
|
|
||||||
message.chat = Mock(spec=Chat)
|
|
||||||
message.chat.id = 123456
|
|
||||||
message.chat.type = "private"
|
|
||||||
message.text = "/start"
|
|
||||||
message.message_id = 1
|
|
||||||
message.forward = AsyncMock()
|
|
||||||
message.answer = AsyncMock()
|
|
||||||
message.answer_sticker = AsyncMock()
|
|
||||||
message.bot.send_message = AsyncMock()
|
|
||||||
return message
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_state(self):
|
|
||||||
"""Создает мок состояния"""
|
|
||||||
state = Mock(spec=FSMContext)
|
|
||||||
state.set_state = AsyncMock()
|
|
||||||
state.get_state = AsyncMock(return_value="START")
|
|
||||||
return state
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_db(self):
|
|
||||||
"""Создает мок базы данных"""
|
|
||||||
db = Mock(spec=BotDB)
|
|
||||||
db.user_exists = Mock(return_value=False)
|
|
||||||
db.add_new_user_in_db = Mock()
|
|
||||||
db.update_date_for_user = Mock()
|
|
||||||
db.update_username_and_full_name = Mock()
|
|
||||||
db.add_post_in_db = Mock()
|
|
||||||
db.update_info_about_stickers = Mock()
|
|
||||||
db.add_new_message_in_db = Mock()
|
|
||||||
return db
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_handle_start_message_new_user(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест обработки команды /start для нового пользователя"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.Path') as mock_path:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.FSInputFile') as mock_fs:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
|
||||||
# Настройка моков
|
|
||||||
mock_keyboard.return_value = Mock()
|
|
||||||
mock_messages.return_value = "Привет!"
|
|
||||||
mock_path.return_value.rglob.return_value = ["sticker1.tgs"]
|
|
||||||
mock_fs.return_value = "sticker_file"
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await handle_start_message(mock_message, mock_state)
|
|
||||||
|
|
||||||
# Проверки
|
|
||||||
mock_message.forward.assert_called_once()
|
|
||||||
mock_db.user_exists.assert_called_once_with(123456)
|
|
||||||
mock_db.add_new_user_in_db.assert_called_once()
|
|
||||||
mock_state.set_state.assert_called_with("START")
|
|
||||||
mock_message.answer_sticker.assert_called_once()
|
|
||||||
mock_message.answer.assert_called_once()
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_handle_start_message_existing_user(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест обработки команды /start для существующего пользователя"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.Path') as mock_path:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.FSInputFile') as mock_fs:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.check_username_and_full_name') as mock_check:
|
|
||||||
# Настройка моков
|
|
||||||
mock_db.user_exists.return_value = True
|
|
||||||
mock_check.return_value = False
|
|
||||||
mock_keyboard.return_value = Mock()
|
|
||||||
mock_messages.return_value = "Привет!"
|
|
||||||
mock_path.return_value.rglob.return_value = ["sticker1.tgs"]
|
|
||||||
mock_fs.return_value = "sticker_file"
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await handle_start_message(mock_message, mock_state)
|
|
||||||
|
|
||||||
# Проверки
|
|
||||||
mock_db.user_exists.assert_called_once_with(123456)
|
|
||||||
mock_db.add_new_user_in_db.assert_not_called()
|
|
||||||
mock_state.set_state.assert_called_with("START")
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_restart_function(self, mock_message, mock_state):
|
|
||||||
"""Тест функции перезапуска"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
|
||||||
mock_keyboard.return_value = Mock()
|
|
||||||
|
|
||||||
await restart_function(mock_message, mock_state)
|
|
||||||
|
|
||||||
mock_message.forward.assert_called_once()
|
|
||||||
mock_message.answer.assert_called_once_with(
|
|
||||||
text='Я перезапущен!',
|
|
||||||
reply_markup=mock_keyboard.return_value
|
|
||||||
)
|
|
||||||
mock_state.set_state.assert_called_with('START')
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_suggest_post(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест функции предложения поста"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
|
||||||
mock_message.text = '📢Предложить свой пост'
|
|
||||||
mock_messages.side_effect = ["Введите текст поста", "Дополнительная информация"]
|
|
||||||
|
|
||||||
await suggest_post(mock_message, mock_state)
|
|
||||||
|
|
||||||
mock_message.forward.assert_called_once()
|
|
||||||
mock_state.set_state.assert_called_with("SUGGEST")
|
|
||||||
assert mock_message.answer.call_count == 2
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_end_message(self, mock_message, mock_state):
|
|
||||||
"""Тест функции прощания"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.Path') as mock_path:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.FSInputFile') as mock_fs:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
|
||||||
mock_message.text = '👋🏼Сказать пока!'
|
|
||||||
mock_path.return_value.rglob.return_value = ["sticker1.tgs"]
|
|
||||||
mock_fs.return_value = "sticker_file"
|
|
||||||
mock_messages.return_value = "До свидания!"
|
|
||||||
|
|
||||||
await end_message(mock_message, mock_state)
|
|
||||||
|
|
||||||
mock_message.forward.assert_called_once()
|
|
||||||
mock_message.answer_sticker.assert_called_once()
|
|
||||||
mock_message.answer.assert_called_once()
|
|
||||||
mock_state.set_state.assert_called_with("START")
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_suggest_router_text(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест обработки текстового поста"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_text_message') as mock_get_text:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.send_text_message') as mock_send:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
|
||||||
# Настройка моков
|
|
||||||
mock_message.content_type = 'text'
|
|
||||||
mock_message.text = 'Тестовый пост'
|
|
||||||
mock_message.media_group_id = None
|
|
||||||
mock_get_text.return_value = 'Обработанный текст'
|
|
||||||
mock_keyboard_post.return_value = Mock()
|
|
||||||
mock_keyboard.return_value = Mock()
|
|
||||||
mock_send.return_value = 123
|
|
||||||
mock_messages.return_value = "Пост отправлен!"
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await suggest_router(mock_message, mock_state)
|
|
||||||
|
|
||||||
# Проверки
|
|
||||||
mock_message.forward.assert_called_once()
|
|
||||||
mock_send.assert_called()
|
|
||||||
mock_db.add_post_in_db.assert_called_once()
|
|
||||||
mock_message.answer.assert_called_once()
|
|
||||||
mock_state.set_state.assert_called_with("START")
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_stickers(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест функции стикеров"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
|
||||||
mock_message.text = '🤪Хочу стикеры'
|
|
||||||
mock_keyboard.return_value = Mock()
|
|
||||||
|
|
||||||
await stickers(mock_message, mock_state)
|
|
||||||
|
|
||||||
mock_message.forward.assert_called_once()
|
|
||||||
mock_db.update_info_about_stickers.assert_called_once_with(user_id=123456)
|
|
||||||
mock_message.answer.assert_called_once()
|
|
||||||
mock_state.set_state.assert_called_with("START")
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_connect_with_admin(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест функции связи с админами"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
mock_message.text = '📩Связаться с админами'
|
|
||||||
mock_messages.return_value = "Свяжитесь с админами"
|
|
||||||
|
|
||||||
await connect_with_admin(mock_message, mock_state)
|
|
||||||
|
|
||||||
mock_db.update_date_for_user.assert_called_once()
|
|
||||||
mock_message.answer.assert_called_once()
|
|
||||||
mock_message.forward.assert_called_once()
|
|
||||||
mock_state.set_state.assert_called_with("PRE_CHAT")
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_resend_message_in_group_pre_chat(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест пересылки сообщения в группу (PRE_CHAT состояние)"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
mock_message.text = 'Тестовое сообщение'
|
|
||||||
mock_keyboard.return_value = Mock()
|
|
||||||
mock_messages.return_value = "Вопрос"
|
|
||||||
mock_state.get_state.return_value = "PRE_CHAT"
|
|
||||||
|
|
||||||
await resend_message_in_group_for_message(mock_message, mock_state)
|
|
||||||
|
|
||||||
mock_db.update_date_for_user.assert_called_once()
|
|
||||||
mock_message.forward.assert_called_once()
|
|
||||||
mock_db.add_new_message_in_db.assert_called_once()
|
|
||||||
mock_message.answer.assert_called_once()
|
|
||||||
mock_state.set_state.assert_called_with("START")
|
|
||||||
|
|
||||||
|
|
||||||
class TestDependencyFactory:
|
|
||||||
"""Тесты для фабрики зависимостей"""
|
|
||||||
|
|
||||||
def test_get_global_instance_singleton(self):
|
|
||||||
"""Тест что get_global_instance возвращает синглтон"""
|
|
||||||
instance1 = get_global_instance()
|
|
||||||
instance2 = get_global_instance()
|
|
||||||
assert instance1 is instance2
|
|
||||||
|
|
||||||
def test_base_dependency_factory_initialization(self):
|
|
||||||
"""Тест инициализации BaseDependencyFactory"""
|
|
||||||
# Этот тест пропускаем из-за сложности мокирования configparser в уже загруженном модуле
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TestBotIntegration:
|
|
||||||
"""Интеграционные тесты бота"""
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_bot_router_registration(self):
|
|
||||||
"""Тест регистрации роутеров в диспетчере"""
|
|
||||||
with patch('helper_bot.main.Bot') as mock_bot_class:
|
|
||||||
with patch('helper_bot.main.Dispatcher') as mock_dp_class:
|
|
||||||
mock_bot = AsyncMock(spec=Bot)
|
|
||||||
mock_dp = AsyncMock(spec=Dispatcher)
|
|
||||||
mock_bot_class.return_value = mock_bot
|
|
||||||
mock_dp_class.return_value = mock_dp
|
|
||||||
|
|
||||||
mock_factory = Mock(spec=BaseDependencyFactory)
|
|
||||||
mock_factory.settings = {
|
|
||||||
'Telegram': {
|
|
||||||
'bot_token': 'test_token',
|
|
||||||
'preview_link': False
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await start_bot(mock_factory)
|
|
||||||
|
|
||||||
# Проверяем, что все роутеры были зарегистрированы
|
|
||||||
mock_dp.include_routers.assert_called_once()
|
|
||||||
call_args = mock_dp.include_routers.call_args[0]
|
|
||||||
assert len(call_args) == 4 # private, callback, group, admin routers
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
pytest.main([__file__, '-v'])
|
|
||||||
@@ -11,7 +11,7 @@ from database.db import BotDB
|
|||||||
def bot():
|
def bot():
|
||||||
"""Фикстура для создания объекта BotDB."""
|
"""Фикстура для создания объекта BotDB."""
|
||||||
current_dir = os.getcwd()
|
current_dir = os.getcwd()
|
||||||
return BotDB(current_dir, "database/test.db")
|
return BotDB(current_dir, "test.db")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True, )
|
@pytest.fixture(autouse=True, )
|
||||||
@@ -38,7 +38,7 @@ def setup_db():
|
|||||||
# Other data
|
# Other data
|
||||||
date = "2024-07-10"
|
date = "2024-07-10"
|
||||||
next_date = "2024-07-11"
|
next_date = "2024-07-11"
|
||||||
conn = sqlite3.connect("database/test.db")
|
conn = sqlite3.connect("test.db")
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
CREATE TABLE IF NOT EXISTS "admins" (
|
CREATE TABLE IF NOT EXISTS "admins" (
|
||||||
@@ -139,12 +139,12 @@ def setup_db():
|
|||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
yield
|
yield
|
||||||
os.remove('database/test.db')
|
os.remove('test.db')
|
||||||
|
|
||||||
|
|
||||||
def test_bot_init(bot):
|
def test_bot_init(bot):
|
||||||
"""Проверяет, что объект BotDB инициализируется с правильным именем файла."""
|
"""Проверяет, что объект BotDB инициализируется с правильным именем файла."""
|
||||||
assert bot.db_file == os.path.join(os.getcwd(), "database", "test.db")
|
assert bot.db_file == os.path.join(os.getcwd(), "test.db")
|
||||||
# Проверьте, что соединения с базой данных нет, так как оно не устанавливается в init
|
# Проверьте, что соединения с базой данных нет, так как оно не устанавливается в init
|
||||||
assert bot.conn is None
|
assert bot.conn is None
|
||||||
assert bot.cursor is None
|
assert bot.cursor is None
|
||||||
@@ -174,7 +174,7 @@ def test_create_table_success(bot):
|
|||||||
bot.create_table(sql_script)
|
bot.create_table(sql_script)
|
||||||
|
|
||||||
# Проверяем, что таблица создана
|
# Проверяем, что таблица создана
|
||||||
conn = sqlite3.connect('database/test.db')
|
conn = sqlite3.connect('test.db')
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='test_table'")
|
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='test_table'")
|
||||||
result = cursor.fetchone()
|
result = cursor.fetchone()
|
||||||
@@ -192,7 +192,7 @@ def test_create_table_error(bot):
|
|||||||
|
|
||||||
|
|
||||||
def test_get_current_version_success(bot):
|
def test_get_current_version_success(bot):
|
||||||
conn = sqlite3.connect('database/test.db')
|
conn = sqlite3.connect('test.db')
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("INSERT INTO migrations (version, script_name) VALUES (123, 'test')")
|
cursor.execute("INSERT INTO migrations (version, script_name) VALUES (123, 'test')")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
@@ -216,7 +216,7 @@ def test_update_version_success(bot):
|
|||||||
bot.update_version(new_version, script_name)
|
bot.update_version(new_version, script_name)
|
||||||
|
|
||||||
# Проверяем, что данные записаны в таблицу
|
# Проверяем, что данные записаны в таблицу
|
||||||
conn = sqlite3.connect('database/test.db')
|
conn = sqlite3.connect('test.db')
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("SELECT * FROM migrations WHERE version = ?", (new_version,))
|
cursor.execute("SELECT * FROM migrations WHERE version = ?", (new_version,))
|
||||||
result = cursor.fetchone()
|
result = cursor.fetchone()
|
||||||
@@ -228,7 +228,7 @@ def test_update_version_success(bot):
|
|||||||
|
|
||||||
|
|
||||||
def test_update_version_integrity_error(bot):
|
def test_update_version_integrity_error(bot):
|
||||||
conn = sqlite3.connect('database/test.db')
|
conn = sqlite3.connect('test.db')
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("INSERT INTO migrations (version, script_name) VALUES (123, 'test')")
|
cursor.execute("INSERT INTO migrations (version, script_name) VALUES (123, 'test')")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
@@ -252,16 +252,17 @@ def test_add_new_user_in_db(bot):
|
|||||||
username = "@petr_ivanov"
|
username = "@petr_ivanov"
|
||||||
is_bot = False
|
is_bot = False
|
||||||
language_code = "ru"
|
language_code = "ru"
|
||||||
|
emoji = '🦀'
|
||||||
date_added = "2024-07-09"
|
date_added = "2024-07-09"
|
||||||
date_changed = "2024-07-09"
|
date_changed = "2024-07-09"
|
||||||
|
|
||||||
# Вызываем функцию add_new_user_in_db
|
# Вызываем функцию add_new_user_in_db
|
||||||
bot.add_new_user_in_db(
|
bot.add_new_user_in_db(
|
||||||
user_id, first_name, full_name, username, is_bot, language_code, date_added, date_changed
|
user_id, first_name, full_name, username, is_bot, language_code, emoji, date_added, date_changed
|
||||||
)
|
)
|
||||||
|
|
||||||
# Проверяем наличие записи в базе данных
|
# Проверяем наличие записи в базе данных
|
||||||
conn = sqlite3.connect('database/test.db')
|
conn = sqlite3.connect('test.db')
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("SELECT * FROM our_users WHERE user_id = ?", (user_id,))
|
cursor.execute("SELECT * FROM our_users WHERE user_id = ?", (user_id,))
|
||||||
result = cursor.fetchone()
|
result = cursor.fetchone()
|
||||||
@@ -285,7 +286,7 @@ def test_add_new_user_in_db_duplicate_user_id(bot, setup_db):
|
|||||||
# Попытка добавить пользователя с тем же user_id
|
# Попытка добавить пользователя с тем же user_id
|
||||||
with pytest.raises(sqlite3.IntegrityError):
|
with pytest.raises(sqlite3.IntegrityError):
|
||||||
bot.add_new_user_in_db(
|
bot.add_new_user_in_db(
|
||||||
user_id, "Марина", "Марина Альфредовна", "marina", False, "bg", "2024-07-09", "2024-07-09"
|
user_id, "Марина", "Марина Альфредовна", "marina", False, "bg", "🦀", "2024-07-09", "2024-07-09"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -297,16 +298,17 @@ def test_add_new_user_in_db_empty_first_name(bot):
|
|||||||
username = "@boris"
|
username = "@boris"
|
||||||
is_bot = False
|
is_bot = False
|
||||||
language_code = "fr"
|
language_code = "fr"
|
||||||
|
emoji = "🦀"
|
||||||
date_added = "2024-07-09"
|
date_added = "2024-07-09"
|
||||||
date_changed = "2024-07-09"
|
date_changed = "2024-07-09"
|
||||||
|
|
||||||
# Вызываем функцию add_new_user_in_db
|
# Вызываем функцию add_new_user_in_db
|
||||||
bot.add_new_user_in_db(
|
bot.add_new_user_in_db(
|
||||||
user_id, first_name, full_name, username, is_bot, language_code, date_added, date_changed
|
user_id, first_name, full_name, username, is_bot, language_code, emoji, date_added, date_changed
|
||||||
)
|
)
|
||||||
|
|
||||||
# Проверяем наличие записи в базе данных
|
# Проверяем наличие записи в базе данных
|
||||||
conn = sqlite3.connect('database/test.db')
|
conn = sqlite3.connect('test.db')
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute(f"SELECT * FROM our_users WHERE user_id = ?", (user_id,))
|
cursor.execute(f"SELECT * FROM our_users WHERE user_id = ?", (user_id,))
|
||||||
result = cursor.fetchone()
|
result = cursor.fetchone()
|
||||||
@@ -388,7 +390,7 @@ def test_get_username_error(bot):
|
|||||||
|
|
||||||
def test_get_all_user_id_empty(bot):
|
def test_get_all_user_id_empty(bot):
|
||||||
"""Проверяет, что функция возвращает пустой список, если в базе нет пользователей."""
|
"""Проверяет, что функция возвращает пустой список, если в базе нет пользователей."""
|
||||||
conn = sqlite3.connect('database/test.db')
|
conn = sqlite3.connect('test.db')
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("DELETE FROM our_users")
|
cursor.execute("DELETE FROM our_users")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
@@ -481,7 +483,7 @@ def test_update_info_about_stickers_success(bot):
|
|||||||
bot.update_info_about_stickers(user_id)
|
bot.update_info_about_stickers(user_id)
|
||||||
|
|
||||||
# Проверяем, что информация обновлена
|
# Проверяем, что информация обновлена
|
||||||
conn = sqlite3.connect('database/test.db')
|
conn = sqlite3.connect('test.db')
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("SELECT has_stickers FROM our_users WHERE user_id = ?", (user_id,))
|
cursor.execute("SELECT has_stickers FROM our_users WHERE user_id = ?", (user_id,))
|
||||||
result = cursor.fetchone()
|
result = cursor.fetchone()
|
||||||
@@ -495,7 +497,7 @@ def test_update_info_about_stickers_not_found(bot):
|
|||||||
bot.update_info_about_stickers(user_id)
|
bot.update_info_about_stickers(user_id)
|
||||||
|
|
||||||
# Проверяем, что база данных не изменилась
|
# Проверяем, что база данных не изменилась
|
||||||
conn = sqlite3.connect('database/test.db')
|
conn = sqlite3.connect('test.db')
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("SELECT COUNT(*) FROM our_users WHERE user_id = ?", (user_id,))
|
cursor.execute("SELECT COUNT(*) FROM our_users WHERE user_id = ?", (user_id,))
|
||||||
result = cursor.fetchone()
|
result = cursor.fetchone()
|
||||||
@@ -512,7 +514,7 @@ def test_update_info_about_stickers_error(bot):
|
|||||||
|
|
||||||
def test_get_users_blacklist_empty(bot):
|
def test_get_users_blacklist_empty(bot):
|
||||||
"""Проверяет, что функция возвращает пустой словарь, если в черном списке нет пользователей."""
|
"""Проверяет, что функция возвращает пустой словарь, если в черном списке нет пользователей."""
|
||||||
conn = sqlite3.connect('database/test.db')
|
conn = sqlite3.connect('test.db')
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("DELETE FROM blacklist")
|
cursor.execute("DELETE FROM blacklist")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
@@ -619,7 +621,7 @@ def test_set_user_blacklist_success(bot):
|
|||||||
assert bot.set_user_blacklist(user_id, user_name, message_for_user, date_to_unban) is None
|
assert bot.set_user_blacklist(user_id, user_name, message_for_user, date_to_unban) is None
|
||||||
|
|
||||||
# Проверяем, что запись добавлена в базу
|
# Проверяем, что запись добавлена в базу
|
||||||
conn = sqlite3.connect('database/test.db')
|
conn = sqlite3.connect('test.db')
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("SELECT * FROM blacklist WHERE user_id = ?", (user_id,))
|
cursor.execute("SELECT * FROM blacklist WHERE user_id = ?", (user_id,))
|
||||||
result = cursor.fetchone()
|
result = cursor.fetchone()
|
||||||
@@ -658,7 +660,7 @@ def test_delete_user_blacklist_success(bot):
|
|||||||
|
|
||||||
@pytest.mark.xfail
|
@pytest.mark.xfail
|
||||||
def test_delete_user_blacklist_not_found(bot):
|
def test_delete_user_blacklist_not_found(bot):
|
||||||
conn = sqlite3.connect('database/test.db')
|
conn = sqlite3.connect('test.db')
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("INSERT INTO blacklist (user_id, user_name, date_to_unban) VALUES (?, ?, ?)",
|
cursor.execute("INSERT INTO blacklist (user_id, user_name, date_to_unban) VALUES (?, ?, ?)",
|
||||||
(12345, "JohnDoe", "2023-12-26"))
|
(12345, "JohnDoe", "2023-12-26"))
|
||||||
@@ -691,7 +693,7 @@ def test_add_new_message_in_db_error(bot):
|
|||||||
def test_update_date_for_user_success(bot):
|
def test_update_date_for_user_success(bot):
|
||||||
bot.update_date_for_user('2024-07-15', 12345)
|
bot.update_date_for_user('2024-07-15', 12345)
|
||||||
|
|
||||||
conn = sqlite3.connect('database/test.db')
|
conn = sqlite3.connect('test.db')
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("SELECT date_changed FROM our_users WHERE user_id = ?", (12345,))
|
cursor.execute("SELECT date_changed FROM our_users WHERE user_id = ?", (12345,))
|
||||||
new_date = cursor.fetchone()[0]
|
new_date = cursor.fetchone()[0]
|
||||||
@@ -741,7 +743,7 @@ def test_get_last_users_from_db_success(bot):
|
|||||||
|
|
||||||
|
|
||||||
def test_get_last_users_from_db_empty(bot):
|
def test_get_last_users_from_db_empty(bot):
|
||||||
conn = sqlite3.connect('database/test.db')
|
conn = sqlite3.connect('test.db')
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("DELETE FROM our_users")
|
cursor.execute("DELETE FROM our_users")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
@@ -768,7 +770,7 @@ def test_get_banned_users_from_db_success(bot):
|
|||||||
|
|
||||||
|
|
||||||
def test_get_banned_users_from_db_empty(bot):
|
def test_get_banned_users_from_db_empty(bot):
|
||||||
conn = sqlite3.connect('database/test.db')
|
conn = sqlite3.connect('test.db')
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("DELETE FROM blacklist")
|
cursor.execute("DELETE FROM blacklist")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
@@ -801,7 +803,7 @@ def test_get_banned_users_from_db_with_limits_success_offset(bot):
|
|||||||
|
|
||||||
|
|
||||||
def test_get_banned_users_from_db_with_limits_empty(bot):
|
def test_get_banned_users_from_db_with_limits_empty(bot):
|
||||||
conn = sqlite3.connect('database/test.db')
|
conn = sqlite3.connect('test.db')
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("DELETE FROM blacklist")
|
cursor.execute("DELETE FROM blacklist")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
@@ -818,7 +820,7 @@ def test_get_banned_users_from_db_with_limits_error(bot):
|
|||||||
|
|
||||||
|
|
||||||
def __drop_table(table_name: str):
|
def __drop_table(table_name: str):
|
||||||
conn = sqlite3.connect('database/test.db')
|
conn = sqlite3.connect('test.db')
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute(f"DROP TABLE {table_name}")
|
cursor.execute(f"DROP TABLE {table_name}")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|||||||
@@ -1,339 +0,0 @@
|
|||||||
# Импортируем моки в самом начале
|
|
||||||
import tests.mocks
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from unittest.mock import Mock, AsyncMock, patch
|
|
||||||
from aiogram.types import Message, User, Chat
|
|
||||||
|
|
||||||
from helper_bot.handlers.private.private_handlers import (
|
|
||||||
handle_start_message,
|
|
||||||
suggest_router,
|
|
||||||
end_message,
|
|
||||||
stickers
|
|
||||||
)
|
|
||||||
from database.db import BotDB
|
|
||||||
|
|
||||||
|
|
||||||
class TestErrorHandling:
|
|
||||||
"""Тесты для обработки ошибок и граничных случаев"""
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_message(self):
|
|
||||||
"""Создает базовый мок сообщения"""
|
|
||||||
message = Mock(spec=Message)
|
|
||||||
message.from_user = Mock(spec=User)
|
|
||||||
message.from_user.id = 123456
|
|
||||||
message.from_user.full_name = "Test User"
|
|
||||||
message.from_user.username = "testuser"
|
|
||||||
message.from_user.first_name = "Test"
|
|
||||||
message.from_user.is_bot = False
|
|
||||||
message.from_user.language_code = "ru"
|
|
||||||
message.chat = Mock(spec=Chat)
|
|
||||||
message.chat.id = 123456
|
|
||||||
message.chat.type = "private"
|
|
||||||
message.message_id = 1
|
|
||||||
message.forward = AsyncMock()
|
|
||||||
message.answer = AsyncMock()
|
|
||||||
message.answer_sticker = AsyncMock()
|
|
||||||
message.bot.send_message = AsyncMock()
|
|
||||||
return message
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_state(self):
|
|
||||||
"""Создает мок состояния"""
|
|
||||||
state = Mock()
|
|
||||||
state.set_state = AsyncMock()
|
|
||||||
state.get_state = AsyncMock(return_value="START")
|
|
||||||
return state
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_db(self):
|
|
||||||
"""Создает мок базы данных"""
|
|
||||||
db = Mock(spec=BotDB)
|
|
||||||
db.user_exists = Mock(return_value=False)
|
|
||||||
db.add_new_user_in_db = Mock()
|
|
||||||
db.update_date_for_user = Mock()
|
|
||||||
db.update_username_and_full_name = Mock()
|
|
||||||
db.add_post_in_db = Mock()
|
|
||||||
db.update_info_about_stickers = Mock()
|
|
||||||
return db
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_handle_start_message_user_without_username(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест обработки пользователя без username"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.Path') as mock_path:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.FSInputFile') as mock_fs:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
|
||||||
# Настройка моков
|
|
||||||
mock_message.from_user.username = None
|
|
||||||
mock_keyboard.return_value = Mock()
|
|
||||||
mock_messages.return_value = "Привет!"
|
|
||||||
mock_path.return_value.rglob.return_value = ["sticker1.tgs"]
|
|
||||||
mock_fs.return_value = "sticker_file"
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await handle_start_message(mock_message, mock_state)
|
|
||||||
|
|
||||||
# Проверки
|
|
||||||
mock_message.bot.send_message.assert_called()
|
|
||||||
# Проверяем, что отправлено сообщение о пользователе без username
|
|
||||||
call_args = mock_message.bot.send_message.call_args_list
|
|
||||||
username_log_call = next(
|
|
||||||
(call for call in call_args if 'без username' in call[1]['text']),
|
|
||||||
None
|
|
||||||
)
|
|
||||||
assert username_log_call is not None
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_handle_start_message_sticker_error(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест обработки ошибки при получении стикера"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.Path') as mock_path:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
|
||||||
# Настройка моков с ошибкой
|
|
||||||
mock_path.return_value.rglob.side_effect = Exception("Sticker error")
|
|
||||||
mock_keyboard.return_value = Mock()
|
|
||||||
mock_messages.return_value = "Привет!"
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await handle_start_message(mock_message, mock_state)
|
|
||||||
|
|
||||||
# Проверки
|
|
||||||
mock_message.bot.send_message.assert_called()
|
|
||||||
# Проверяем, что отправлено сообщение об ошибке
|
|
||||||
call_args = mock_message.bot.send_message.call_args_list
|
|
||||||
error_call = next(
|
|
||||||
(call for call in call_args if 'ошибка при получении стикеров' in call[1]['text']),
|
|
||||||
None
|
|
||||||
)
|
|
||||||
assert error_call is not None
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_handle_start_message_message_error(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест обработки ошибки при отправке приветственного сообщения"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.Path') as mock_path:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.FSInputFile') as mock_fs:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
|
||||||
# Настройка моков
|
|
||||||
mock_keyboard.return_value = Mock()
|
|
||||||
mock_messages.side_effect = Exception("Message error")
|
|
||||||
mock_path.return_value.rglob.return_value = ["sticker1.tgs"]
|
|
||||||
mock_fs.return_value = "sticker_file"
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await handle_start_message(mock_message, mock_state)
|
|
||||||
|
|
||||||
# Проверки
|
|
||||||
mock_message.bot.send_message.assert_called()
|
|
||||||
# Проверяем, что отправлено сообщение об ошибке
|
|
||||||
call_args = mock_message.bot.send_message.call_args_list
|
|
||||||
# Проверяем, что было отправлено хотя бы одно сообщение
|
|
||||||
assert len(call_args) > 0
|
|
||||||
# Проверяем, что в одном из сообщений есть текст об ошибке
|
|
||||||
error_found = False
|
|
||||||
for call in call_args:
|
|
||||||
text = call.kwargs.get('text', '') or (call[0][1] if len(call[0]) > 1 else '')
|
|
||||||
if 'Произошла ошибка' in text:
|
|
||||||
error_found = True
|
|
||||||
break
|
|
||||||
assert error_found
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_suggest_router_exception_handling(self, mock_message, mock_state):
|
|
||||||
"""Тест обработки исключений в suggest_router"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB') as mock_db:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_text_message') as mock_get_text:
|
|
||||||
# Настройка моков с ошибкой
|
|
||||||
mock_message.content_type = 'text'
|
|
||||||
mock_message.text = 'Тестовый пост'
|
|
||||||
mock_message.media_group_id = None
|
|
||||||
mock_get_text.side_effect = Exception("Processing error")
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await suggest_router(mock_message, mock_state)
|
|
||||||
|
|
||||||
# Проверки
|
|
||||||
mock_message.bot.send_message.assert_called_once()
|
|
||||||
call_args = mock_message.bot.send_message.call_args
|
|
||||||
assert 'Произошла ошибка' in call_args[1]['text']
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_end_message_sticker_error(self, mock_message, mock_state):
|
|
||||||
"""Тест обработки ошибки при получении стикера в end_message"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.Path') as mock_path:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
|
||||||
# Настройка моков с ошибкой
|
|
||||||
mock_message.text = '👋🏼Сказать пока!'
|
|
||||||
mock_path.return_value.rglob.side_effect = Exception("Sticker error")
|
|
||||||
mock_messages.return_value = "До свидания!"
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await end_message(mock_message, mock_state)
|
|
||||||
|
|
||||||
# Проверки
|
|
||||||
mock_message.bot.send_message.assert_called()
|
|
||||||
call_args = mock_message.bot.send_message.call_args_list
|
|
||||||
# Проверяем, что в одном из сообщений есть текст об ошибке
|
|
||||||
error_found = False
|
|
||||||
for call in call_args:
|
|
||||||
text = call.kwargs.get('text', '') or (call[0][1] if len(call[0]) > 1 else '')
|
|
||||||
if 'Произошла ошибка' in text:
|
|
||||||
error_found = True
|
|
||||||
break
|
|
||||||
assert error_found
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_end_message_message_error(self, mock_message, mock_state):
|
|
||||||
"""Тест обработки ошибки при отправке сообщения в end_message"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.Path') as mock_path:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.FSInputFile') as mock_fs:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
|
||||||
# Настройка моков
|
|
||||||
mock_message.text = '👋🏼Сказать пока!'
|
|
||||||
mock_path.return_value.rglob.return_value = ["sticker1.tgs"]
|
|
||||||
mock_fs.return_value = "sticker_file"
|
|
||||||
mock_messages.side_effect = Exception("Message error")
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await end_message(mock_message, mock_state)
|
|
||||||
|
|
||||||
# Проверки
|
|
||||||
mock_message.bot.send_message.assert_called()
|
|
||||||
call_args = mock_message.bot.send_message.call_args_list
|
|
||||||
# Проверяем, что в одном из сообщений есть текст об ошибке
|
|
||||||
error_found = False
|
|
||||||
for call in call_args:
|
|
||||||
text = call.kwargs.get('text', '') or (call[0][1] if len(call[0]) > 1 else '')
|
|
||||||
if 'Произошла ошибка' in text:
|
|
||||||
error_found = True
|
|
||||||
break
|
|
||||||
assert error_found
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_stickers_exception_handling(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест обработки исключений в stickers"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
|
||||||
# Настройка моков с ошибкой
|
|
||||||
mock_message.text = '🤪Хочу стикеры'
|
|
||||||
mock_db.update_info_about_stickers.side_effect = Exception("Database error")
|
|
||||||
mock_keyboard.return_value = Mock()
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await stickers(mock_message, mock_state)
|
|
||||||
|
|
||||||
# Проверки
|
|
||||||
mock_message.bot.send_message.assert_called_once()
|
|
||||||
call_args = mock_message.bot.send_message.call_args
|
|
||||||
assert 'Произошла ошибка' in call_args[1]['text']
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_suggest_router_empty_text(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест обработки пустого текста"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_text_message') as mock_get_text:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.send_text_message') as mock_send:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
|
||||||
# Настройка моков
|
|
||||||
mock_message.content_type = 'text'
|
|
||||||
mock_message.text = ''
|
|
||||||
mock_message.media_group_id = None
|
|
||||||
mock_get_text.return_value = ''
|
|
||||||
mock_keyboard_post.return_value = Mock()
|
|
||||||
mock_keyboard.return_value = Mock()
|
|
||||||
mock_send.return_value = 123
|
|
||||||
mock_messages.return_value = "Пост отправлен!"
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await suggest_router(mock_message, mock_state)
|
|
||||||
|
|
||||||
# Проверки - даже пустой текст должен обрабатываться
|
|
||||||
mock_message.forward.assert_called_once()
|
|
||||||
mock_send.assert_called()
|
|
||||||
mock_db.add_post_in_db.assert_called_once()
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_suggest_router_photo_without_caption(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест обработки фото без подписи"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.send_photo_message') as mock_send_photo:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.add_in_db_media') as mock_add_media:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
|
||||||
# Настройка моков для фото без подписи
|
|
||||||
mock_message.content_type = 'photo'
|
|
||||||
mock_message.caption = None
|
|
||||||
mock_message.media_group_id = None
|
|
||||||
mock_message.photo = [Mock()]
|
|
||||||
mock_message.photo[-1].file_id = 'photo_file_id'
|
|
||||||
|
|
||||||
mock_keyboard_post.return_value = Mock()
|
|
||||||
mock_keyboard.return_value = Mock()
|
|
||||||
mock_send_photo.return_value = Mock()
|
|
||||||
mock_send_photo.return_value.message_id = 123
|
|
||||||
mock_send_photo.return_value.caption = ''
|
|
||||||
mock_messages.return_value = "Фото отправлено!"
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await suggest_router(mock_message, mock_state)
|
|
||||||
|
|
||||||
# Проверки
|
|
||||||
mock_message.forward.assert_called_once()
|
|
||||||
mock_send_photo.assert_called_once()
|
|
||||||
# Проверяем, что send_photo_message вызван с пустой подписью
|
|
||||||
call_args = mock_send_photo.call_args
|
|
||||||
assert call_args.kwargs.get('caption', '') == ''
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_suggest_router_media_group_without_caption(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест обработки медиагруппы без подписи"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.prepare_media_group_from_middlewares') as mock_prepare:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.send_media_group_message_to_private_chat') as mock_send_group:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.send_text_message') as mock_send_text:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
|
||||||
# Настройка моков для медиагруппы без подписи
|
|
||||||
mock_message.media_group_id = 'group_123'
|
|
||||||
mock_message.content_type = 'photo'
|
|
||||||
|
|
||||||
# Создаем мок альбома без подписи
|
|
||||||
album = [mock_message]
|
|
||||||
album[0].caption = None
|
|
||||||
|
|
||||||
mock_keyboard_post.return_value = Mock()
|
|
||||||
mock_keyboard.return_value = Mock()
|
|
||||||
mock_prepare.return_value = ['media1', 'media2']
|
|
||||||
mock_send_group.return_value = 123
|
|
||||||
mock_send_text.return_value = 456
|
|
||||||
mock_messages.return_value = "Медиагруппа отправлена!"
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await suggest_router(mock_message, mock_state, album)
|
|
||||||
|
|
||||||
# Проверки
|
|
||||||
mock_prepare.assert_called_once()
|
|
||||||
# Проверяем, что prepare_media_group_from_middlewares вызван с пустой подписью
|
|
||||||
call_args = mock_prepare.call_args
|
|
||||||
assert call_args.kwargs.get('post_caption', '') == ''
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
pytest.main([__file__, '-v'])
|
|
||||||
@@ -1,330 +0,0 @@
|
|||||||
import pytest
|
|
||||||
from unittest.mock import Mock, patch
|
|
||||||
from aiogram.types import ReplyKeyboardMarkup, KeyboardButton, InlineKeyboardMarkup, InlineKeyboardButton
|
|
||||||
|
|
||||||
from helper_bot.keyboards.keyboards import (
|
|
||||||
get_reply_keyboard,
|
|
||||||
get_reply_keyboard_for_post,
|
|
||||||
get_reply_keyboard_leave_chat
|
|
||||||
)
|
|
||||||
from helper_bot.filters.main import ChatTypeFilter
|
|
||||||
from database.db import BotDB
|
|
||||||
|
|
||||||
|
|
||||||
class TestKeyboards:
|
|
||||||
"""Тесты для клавиатур"""
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_db(self):
|
|
||||||
"""Создает мок базы данных"""
|
|
||||||
db = Mock(spec=BotDB)
|
|
||||||
db.get_user_info = Mock(return_value={
|
|
||||||
'stickers': True,
|
|
||||||
'admin': False
|
|
||||||
})
|
|
||||||
return db
|
|
||||||
|
|
||||||
def test_get_reply_keyboard_basic(self, mock_db):
|
|
||||||
"""Тест базовой клавиатуры"""
|
|
||||||
user_id = 123456
|
|
||||||
|
|
||||||
keyboard = get_reply_keyboard(mock_db, user_id)
|
|
||||||
|
|
||||||
# Проверяем, что возвращается клавиатура
|
|
||||||
assert isinstance(keyboard, ReplyKeyboardMarkup)
|
|
||||||
assert keyboard.keyboard is not None
|
|
||||||
assert len(keyboard.keyboard) > 0
|
|
||||||
|
|
||||||
# Проверяем наличие основных кнопок
|
|
||||||
all_buttons = []
|
|
||||||
for row in keyboard.keyboard:
|
|
||||||
for button in row:
|
|
||||||
all_buttons.append(button.text)
|
|
||||||
|
|
||||||
# Проверяем наличие основных кнопок
|
|
||||||
assert '📢Предложить свой пост' in all_buttons
|
|
||||||
assert '👋🏼Сказать пока!' in all_buttons
|
|
||||||
assert '📩Связаться с админами' in all_buttons
|
|
||||||
|
|
||||||
def test_get_reply_keyboard_with_stickers(self, mock_db):
|
|
||||||
"""Тест клавиатуры со стикерами"""
|
|
||||||
user_id = 123456
|
|
||||||
# Мокаем метод get_info_about_stickers
|
|
||||||
mock_db.get_info_about_stickers = Mock(return_value=False)
|
|
||||||
|
|
||||||
keyboard = get_reply_keyboard(mock_db, user_id)
|
|
||||||
|
|
||||||
all_buttons = []
|
|
||||||
for row in keyboard.keyboard:
|
|
||||||
for button in row:
|
|
||||||
all_buttons.append(button.text)
|
|
||||||
|
|
||||||
# Проверяем наличие кнопки стикеров
|
|
||||||
assert '🤪Хочу стикеры' in all_buttons
|
|
||||||
|
|
||||||
def test_get_reply_keyboard_without_stickers(self, mock_db):
|
|
||||||
"""Тест клавиатуры без стикеров"""
|
|
||||||
user_id = 123456
|
|
||||||
# Мокаем метод get_info_about_stickers
|
|
||||||
mock_db.get_info_about_stickers = Mock(return_value=True)
|
|
||||||
|
|
||||||
keyboard = get_reply_keyboard(mock_db, user_id)
|
|
||||||
|
|
||||||
all_buttons = []
|
|
||||||
for row in keyboard.keyboard:
|
|
||||||
for button in row:
|
|
||||||
all_buttons.append(button.text)
|
|
||||||
|
|
||||||
# Проверяем отсутствие кнопки стикеров
|
|
||||||
assert '🤪Хочу стикеры' not in all_buttons
|
|
||||||
|
|
||||||
def test_get_reply_keyboard_admin(self, mock_db):
|
|
||||||
"""Тест клавиатуры для админа"""
|
|
||||||
user_id = 123456
|
|
||||||
# Мокаем метод get_info_about_stickers
|
|
||||||
mock_db.get_info_about_stickers = Mock(return_value=False)
|
|
||||||
|
|
||||||
keyboard = get_reply_keyboard(mock_db, user_id)
|
|
||||||
|
|
||||||
all_buttons = []
|
|
||||||
for row in keyboard.keyboard:
|
|
||||||
for button in row:
|
|
||||||
all_buttons.append(button.text)
|
|
||||||
|
|
||||||
# Проверяем наличие основных кнопок
|
|
||||||
assert '📢Предложить свой пост' in all_buttons
|
|
||||||
assert '👋🏼Сказать пока!' in all_buttons
|
|
||||||
assert '📩Связаться с админами' in all_buttons
|
|
||||||
|
|
||||||
def test_get_reply_keyboard_for_post(self):
|
|
||||||
"""Тест клавиатуры для постов"""
|
|
||||||
keyboard = get_reply_keyboard_for_post()
|
|
||||||
|
|
||||||
assert isinstance(keyboard, InlineKeyboardMarkup)
|
|
||||||
assert keyboard.inline_keyboard is not None
|
|
||||||
assert len(keyboard.inline_keyboard) > 0
|
|
||||||
|
|
||||||
all_buttons = []
|
|
||||||
for row in keyboard.inline_keyboard:
|
|
||||||
for button in row:
|
|
||||||
all_buttons.append(button.text)
|
|
||||||
|
|
||||||
# Проверяем наличие кнопок для постов
|
|
||||||
assert 'Опубликовать' in all_buttons
|
|
||||||
assert 'Отклонить' in all_buttons
|
|
||||||
|
|
||||||
def test_get_reply_keyboard_leave_chat(self):
|
|
||||||
"""Тест клавиатуры для выхода из чата"""
|
|
||||||
keyboard = get_reply_keyboard_leave_chat()
|
|
||||||
|
|
||||||
assert isinstance(keyboard, ReplyKeyboardMarkup)
|
|
||||||
assert keyboard.keyboard is not None
|
|
||||||
assert len(keyboard.keyboard) > 0
|
|
||||||
|
|
||||||
all_buttons = []
|
|
||||||
for row in keyboard.keyboard:
|
|
||||||
for button in row:
|
|
||||||
all_buttons.append(button.text)
|
|
||||||
|
|
||||||
# Проверяем наличие кнопки выхода
|
|
||||||
assert 'Выйти из чата' in all_buttons
|
|
||||||
|
|
||||||
def test_keyboard_resize(self):
|
|
||||||
"""Тест настройки resize клавиатуры"""
|
|
||||||
keyboard = get_reply_keyboard_for_post()
|
|
||||||
|
|
||||||
# Проверяем, что клавиатура настроена правильно
|
|
||||||
# InlineKeyboardMarkup не имеет resize_keyboard
|
|
||||||
assert isinstance(keyboard, InlineKeyboardMarkup)
|
|
||||||
|
|
||||||
def test_keyboard_one_time(self):
|
|
||||||
"""Тест настройки one_time клавиатуры"""
|
|
||||||
keyboard = get_reply_keyboard_leave_chat()
|
|
||||||
|
|
||||||
# Проверяем, что клавиатура настроена правильно
|
|
||||||
assert hasattr(keyboard, 'one_time_keyboard')
|
|
||||||
assert keyboard.one_time_keyboard is True
|
|
||||||
|
|
||||||
|
|
||||||
class TestChatTypeFilter:
|
|
||||||
"""Тесты для фильтра типа чата"""
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_message(self):
|
|
||||||
"""Создает мок сообщения"""
|
|
||||||
message = Mock()
|
|
||||||
message.chat = Mock()
|
|
||||||
return message
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_chat_type_filter_private(self, mock_message):
|
|
||||||
"""Тест фильтра для приватного чата"""
|
|
||||||
mock_message.chat.type = "private"
|
|
||||||
|
|
||||||
filter_private = ChatTypeFilter(chat_type=["private"])
|
|
||||||
filter_group = ChatTypeFilter(chat_type=["group"])
|
|
||||||
filter_supergroup = ChatTypeFilter(chat_type=["supergroup"])
|
|
||||||
|
|
||||||
# Проверяем, что фильтр работает правильно
|
|
||||||
assert await filter_private(mock_message) is True
|
|
||||||
assert await filter_group(mock_message) is False
|
|
||||||
assert await filter_supergroup(mock_message) is False
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_chat_type_filter_group(self, mock_message):
|
|
||||||
"""Тест фильтра для группового чата"""
|
|
||||||
mock_message.chat.type = "group"
|
|
||||||
|
|
||||||
filter_private = ChatTypeFilter(chat_type=["private"])
|
|
||||||
filter_group = ChatTypeFilter(chat_type=["group"])
|
|
||||||
filter_supergroup = ChatTypeFilter(chat_type=["supergroup"])
|
|
||||||
|
|
||||||
# Проверяем, что фильтр работает правильно
|
|
||||||
assert await filter_private(mock_message) is False
|
|
||||||
assert await filter_group(mock_message) is True
|
|
||||||
assert await filter_supergroup(mock_message) is False
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_chat_type_filter_supergroup(self, mock_message):
|
|
||||||
"""Тест фильтра для супергруппы"""
|
|
||||||
mock_message.chat.type = "supergroup"
|
|
||||||
|
|
||||||
filter_private = ChatTypeFilter(chat_type=["private"])
|
|
||||||
filter_group = ChatTypeFilter(chat_type=["group"])
|
|
||||||
filter_supergroup = ChatTypeFilter(chat_type=["supergroup"])
|
|
||||||
|
|
||||||
# Проверяем, что фильтр работает правильно
|
|
||||||
assert await filter_private(mock_message) is False
|
|
||||||
assert await filter_group(mock_message) is False
|
|
||||||
assert await filter_supergroup(mock_message) is True
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_chat_type_filter_multiple_types(self, mock_message):
|
|
||||||
"""Тест фильтра с несколькими типами чатов"""
|
|
||||||
filter_private_group = ChatTypeFilter(chat_type=["private", "group"])
|
|
||||||
filter_all = ChatTypeFilter(chat_type=["private", "group", "supergroup"])
|
|
||||||
|
|
||||||
# Тест для приватного чата
|
|
||||||
mock_message.chat.type = "private"
|
|
||||||
assert await filter_private_group(mock_message) is True
|
|
||||||
assert await filter_all(mock_message) is True
|
|
||||||
|
|
||||||
# Тест для группового чата
|
|
||||||
mock_message.chat.type = "group"
|
|
||||||
assert await filter_private_group(mock_message) is True
|
|
||||||
assert await filter_all(mock_message) is True
|
|
||||||
|
|
||||||
# Тест для супергруппы
|
|
||||||
mock_message.chat.type = "supergroup"
|
|
||||||
assert await filter_private_group(mock_message) is False
|
|
||||||
assert await filter_all(mock_message) is True
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_chat_type_filter_channel(self, mock_message):
|
|
||||||
"""Тест фильтра для канала"""
|
|
||||||
mock_message.chat.type = "channel"
|
|
||||||
|
|
||||||
filter_channel = ChatTypeFilter(chat_type=["channel"])
|
|
||||||
filter_private = ChatTypeFilter(chat_type=["private"])
|
|
||||||
|
|
||||||
assert await filter_channel(mock_message) is True
|
|
||||||
assert await filter_private(mock_message) is False
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_chat_type_filter_empty_list(self, mock_message):
|
|
||||||
"""Тест фильтра с пустым списком типов"""
|
|
||||||
mock_message.chat.type = "private"
|
|
||||||
|
|
||||||
filter_empty = ChatTypeFilter(chat_type=[])
|
|
||||||
|
|
||||||
# Фильтр с пустым списком должен возвращать False
|
|
||||||
assert await filter_empty(mock_message) is False
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_chat_type_filter_invalid_type(self, mock_message):
|
|
||||||
"""Тест фильтра с несуществующим типом чата"""
|
|
||||||
mock_message.chat.type = "invalid_type"
|
|
||||||
|
|
||||||
filter_private = ChatTypeFilter(chat_type=["private"])
|
|
||||||
filter_invalid = ChatTypeFilter(chat_type=["invalid_type"])
|
|
||||||
|
|
||||||
assert await filter_private(mock_message) is False
|
|
||||||
assert await filter_invalid(mock_message) is True
|
|
||||||
|
|
||||||
|
|
||||||
class TestKeyboardIntegration:
|
|
||||||
"""Интеграционные тесты клавиатур"""
|
|
||||||
|
|
||||||
def test_keyboard_structure_consistency(self):
|
|
||||||
"""Тест консистентности структуры клавиатур"""
|
|
||||||
# Мокаем базу данных
|
|
||||||
mock_db = Mock(spec=BotDB)
|
|
||||||
mock_db.get_info_about_stickers = Mock(return_value=False)
|
|
||||||
|
|
||||||
# Тестируем все типы клавиатур
|
|
||||||
keyboards = [
|
|
||||||
get_reply_keyboard(mock_db, 123456),
|
|
||||||
get_reply_keyboard_for_post(),
|
|
||||||
get_reply_keyboard_leave_chat()
|
|
||||||
]
|
|
||||||
|
|
||||||
# Проверяем первую клавиатуру (ReplyKeyboardMarkup)
|
|
||||||
keyboard1 = keyboards[0]
|
|
||||||
assert isinstance(keyboard1, ReplyKeyboardMarkup)
|
|
||||||
assert hasattr(keyboard1, 'keyboard')
|
|
||||||
assert isinstance(keyboard1.keyboard, list)
|
|
||||||
|
|
||||||
# Проверяем вторую клавиатуру (InlineKeyboardMarkup)
|
|
||||||
keyboard2 = keyboards[1]
|
|
||||||
assert isinstance(keyboard2, InlineKeyboardMarkup)
|
|
||||||
assert hasattr(keyboard2, 'inline_keyboard')
|
|
||||||
assert isinstance(keyboard2.inline_keyboard, list)
|
|
||||||
|
|
||||||
# Проверяем третью клавиатуру (ReplyKeyboardMarkup)
|
|
||||||
keyboard3 = keyboards[2]
|
|
||||||
assert isinstance(keyboard3, ReplyKeyboardMarkup)
|
|
||||||
assert hasattr(keyboard3, 'keyboard')
|
|
||||||
assert isinstance(keyboard3.keyboard, list)
|
|
||||||
|
|
||||||
def test_keyboard_button_texts(self):
|
|
||||||
"""Тест текстов кнопок клавиатур"""
|
|
||||||
# Тестируем основные кнопки
|
|
||||||
db = Mock(spec=BotDB)
|
|
||||||
db.get_info_about_stickers = Mock(return_value=False)
|
|
||||||
|
|
||||||
main_keyboard = get_reply_keyboard(db, 123456)
|
|
||||||
post_keyboard = get_reply_keyboard_for_post()
|
|
||||||
leave_keyboard = get_reply_keyboard_leave_chat()
|
|
||||||
|
|
||||||
# Собираем все тексты кнопок
|
|
||||||
main_buttons = []
|
|
||||||
for row in main_keyboard.keyboard:
|
|
||||||
for button in row:
|
|
||||||
main_buttons.append(button.text)
|
|
||||||
|
|
||||||
post_buttons = []
|
|
||||||
for row in post_keyboard.inline_keyboard:
|
|
||||||
for button in row:
|
|
||||||
post_buttons.append(button.text)
|
|
||||||
|
|
||||||
leave_buttons = []
|
|
||||||
for row in leave_keyboard.keyboard:
|
|
||||||
for button in row:
|
|
||||||
leave_buttons.append(button.text)
|
|
||||||
|
|
||||||
# Проверяем наличие основных кнопок
|
|
||||||
assert '📢Предложить свой пост' in main_buttons
|
|
||||||
assert '👋🏼Сказать пока!' in main_buttons
|
|
||||||
assert '📩Связаться с админами' in main_buttons
|
|
||||||
assert '🤪Хочу стикеры' in main_buttons
|
|
||||||
|
|
||||||
# Проверяем кнопки для постов
|
|
||||||
assert 'Опубликовать' in post_buttons
|
|
||||||
assert 'Отклонить' in post_buttons
|
|
||||||
|
|
||||||
# Проверяем кнопку выхода
|
|
||||||
assert 'Выйти из чата' in leave_buttons
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
pytest.main([__file__, '-v'])
|
|
||||||
@@ -1,292 +0,0 @@
|
|||||||
# Импортируем моки в самом начале
|
|
||||||
import tests.mocks
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from unittest.mock import Mock, AsyncMock, patch
|
|
||||||
from aiogram.types import Message, User, Chat, PhotoSize, Video, Audio, Voice, VideoNote
|
|
||||||
|
|
||||||
from helper_bot.handlers.private.private_handlers import suggest_router
|
|
||||||
from database.db import BotDB
|
|
||||||
|
|
||||||
|
|
||||||
class TestMediaHandlers:
|
|
||||||
"""Тесты для обработки медиа-контента"""
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_message(self):
|
|
||||||
"""Создает базовый мок сообщения"""
|
|
||||||
message = Mock(spec=Message)
|
|
||||||
message.from_user = Mock(spec=User)
|
|
||||||
message.from_user.id = 123456
|
|
||||||
message.from_user.full_name = "Test User"
|
|
||||||
message.from_user.username = "testuser"
|
|
||||||
message.from_user.first_name = "Test"
|
|
||||||
message.chat = Mock(spec=Chat)
|
|
||||||
message.chat.id = 123456
|
|
||||||
message.chat.type = "private"
|
|
||||||
message.message_id = 1
|
|
||||||
message.forward = AsyncMock()
|
|
||||||
message.answer = AsyncMock()
|
|
||||||
message.bot.send_message = AsyncMock()
|
|
||||||
return message
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_state(self):
|
|
||||||
"""Создает мок состояния"""
|
|
||||||
state = Mock()
|
|
||||||
state.set_state = AsyncMock()
|
|
||||||
return state
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_db(self):
|
|
||||||
"""Создает мок базы данных"""
|
|
||||||
db = Mock(spec=BotDB)
|
|
||||||
db.add_post_in_db = Mock()
|
|
||||||
return db
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_suggest_router_photo(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест обработки фото"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_text_message') as mock_get_text:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.send_photo_message') as mock_send_photo:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.add_in_db_media') as mock_add_media:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
|
||||||
# Настройка моков для фото
|
|
||||||
mock_message.content_type = 'photo'
|
|
||||||
mock_message.caption = 'Тестовое фото'
|
|
||||||
mock_message.media_group_id = None
|
|
||||||
mock_message.photo = [Mock(spec=PhotoSize)]
|
|
||||||
mock_message.photo[-1].file_id = 'photo_file_id'
|
|
||||||
|
|
||||||
mock_get_text.return_value = 'Обработанная подпись'
|
|
||||||
mock_keyboard_post.return_value = Mock()
|
|
||||||
mock_keyboard.return_value = Mock()
|
|
||||||
mock_send_photo.return_value = Mock()
|
|
||||||
mock_send_photo.return_value.message_id = 123
|
|
||||||
mock_send_photo.return_value.caption = 'Обработанная подпись'
|
|
||||||
mock_messages.return_value = "Фото отправлено!"
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await suggest_router(mock_message, mock_state)
|
|
||||||
|
|
||||||
# Проверки
|
|
||||||
mock_message.forward.assert_called_once()
|
|
||||||
mock_send_photo.assert_called_once()
|
|
||||||
mock_db.add_post_in_db.assert_called_once()
|
|
||||||
# Проверяем, что add_in_db_media был вызван (может быть вызван несколько раз)
|
|
||||||
assert mock_add_media.call_count >= 1
|
|
||||||
mock_message.answer.assert_called_once()
|
|
||||||
mock_state.set_state.assert_called_with("START")
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_suggest_router_video(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест обработки видео"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_text_message') as mock_get_text:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.send_video_message') as mock_send_video:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.add_in_db_media') as mock_add_media:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
|
||||||
# Настройка моков для видео
|
|
||||||
mock_message.content_type = 'video'
|
|
||||||
mock_message.caption = 'Тестовое видео'
|
|
||||||
mock_message.media_group_id = None
|
|
||||||
mock_message.video = Mock(spec=Video)
|
|
||||||
mock_message.video.file_id = 'video_file_id'
|
|
||||||
|
|
||||||
mock_get_text.return_value = 'Обработанная подпись'
|
|
||||||
mock_keyboard_post.return_value = Mock()
|
|
||||||
mock_keyboard.return_value = Mock()
|
|
||||||
mock_send_video.return_value = Mock()
|
|
||||||
mock_send_video.return_value.message_id = 123
|
|
||||||
mock_send_video.return_value.caption = 'Обработанная подпись'
|
|
||||||
mock_messages.return_value = "Видео отправлено!"
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await suggest_router(mock_message, mock_state)
|
|
||||||
|
|
||||||
# Проверки
|
|
||||||
mock_message.forward.assert_called_once()
|
|
||||||
mock_send_video.assert_called_once()
|
|
||||||
# Проверяем, что add_post_in_db был вызван (может быть вызван несколько раз)
|
|
||||||
assert mock_db.add_post_in_db.call_count >= 1
|
|
||||||
# Проверяем, что add_in_db_media был вызван (может быть вызван несколько раз)
|
|
||||||
assert mock_add_media.call_count >= 1
|
|
||||||
mock_message.answer.assert_called_once()
|
|
||||||
mock_state.set_state.assert_called_with("START")
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_suggest_router_video_note(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест обработки видеокружка"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.send_video_note_message') as mock_send_video_note:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.add_in_db_media') as mock_add_media:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
|
||||||
# Настройка моков для видеокружка
|
|
||||||
mock_message.content_type = 'video_note'
|
|
||||||
mock_message.media_group_id = None
|
|
||||||
mock_message.video_note = Mock(spec=VideoNote)
|
|
||||||
mock_message.video_note.file_id = 'video_note_file_id'
|
|
||||||
|
|
||||||
mock_keyboard_post.return_value = Mock()
|
|
||||||
mock_keyboard.return_value = Mock()
|
|
||||||
mock_send_video_note.return_value = Mock()
|
|
||||||
mock_send_video_note.return_value.message_id = 123
|
|
||||||
mock_messages.return_value = "Видеокружок отправлен!"
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await suggest_router(mock_message, mock_state)
|
|
||||||
|
|
||||||
# Проверки
|
|
||||||
mock_message.forward.assert_called_once()
|
|
||||||
mock_send_video_note.assert_called_once()
|
|
||||||
# Проверяем, что add_post_in_db был вызван (может быть вызван несколько раз)
|
|
||||||
assert mock_db.add_post_in_db.call_count >= 1
|
|
||||||
# Проверяем, что add_in_db_media был вызван (может быть вызван несколько раз)
|
|
||||||
assert mock_add_media.call_count >= 1
|
|
||||||
mock_message.answer.assert_called_once()
|
|
||||||
mock_state.set_state.assert_called_with("START")
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_suggest_router_audio(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест обработки аудио"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_text_message') as mock_get_text:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.send_audio_message') as mock_send_audio:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.add_in_db_media') as mock_add_media:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
|
||||||
# Настройка моков для аудио
|
|
||||||
mock_message.content_type = 'audio'
|
|
||||||
mock_message.caption = 'Тестовое аудио'
|
|
||||||
mock_message.media_group_id = None
|
|
||||||
mock_message.audio = Mock(spec=Audio)
|
|
||||||
mock_message.audio.file_id = 'audio_file_id'
|
|
||||||
|
|
||||||
mock_get_text.return_value = 'Обработанная подпись'
|
|
||||||
mock_keyboard_post.return_value = Mock()
|
|
||||||
mock_keyboard.return_value = Mock()
|
|
||||||
mock_send_audio.return_value = Mock()
|
|
||||||
mock_send_audio.return_value.message_id = 123
|
|
||||||
mock_send_audio.return_value.caption = 'Обработанная подпись'
|
|
||||||
mock_messages.return_value = "Аудио отправлено!"
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await suggest_router(mock_message, mock_state)
|
|
||||||
|
|
||||||
# Проверки
|
|
||||||
mock_message.forward.assert_called_once()
|
|
||||||
mock_send_audio.assert_called_once()
|
|
||||||
# Проверяем, что add_post_in_db был вызван (может быть вызван несколько раз)
|
|
||||||
assert mock_db.add_post_in_db.call_count >= 1
|
|
||||||
# Проверяем, что add_in_db_media был вызван (может быть вызван несколько раз)
|
|
||||||
assert mock_add_media.call_count >= 1
|
|
||||||
mock_message.answer.assert_called_once()
|
|
||||||
mock_state.set_state.assert_called_with("START")
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_suggest_router_voice(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест обработки голосового сообщения"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.send_voice_message') as mock_send_voice:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.add_in_db_media') as mock_add_media:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
|
||||||
# Настройка моков для голосового сообщения
|
|
||||||
mock_message.content_type = 'voice'
|
|
||||||
mock_message.media_group_id = None
|
|
||||||
mock_message.voice = Mock(spec=Voice)
|
|
||||||
mock_message.voice.file_id = 'voice_file_id'
|
|
||||||
|
|
||||||
mock_keyboard_post.return_value = Mock()
|
|
||||||
mock_keyboard.return_value = Mock()
|
|
||||||
mock_send_voice.return_value = Mock()
|
|
||||||
mock_send_voice.return_value.message_id = 123
|
|
||||||
mock_messages.return_value = "Голосовое сообщение отправлено!"
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await suggest_router(mock_message, mock_state)
|
|
||||||
|
|
||||||
# Проверки
|
|
||||||
mock_message.forward.assert_called_once()
|
|
||||||
mock_send_voice.assert_called_once()
|
|
||||||
mock_db.add_post_in_db.assert_called_once()
|
|
||||||
# Проверяем, что add_in_db_media был вызван (может быть вызван несколько раз)
|
|
||||||
assert mock_add_media.call_count >= 1
|
|
||||||
mock_message.answer.assert_called_once()
|
|
||||||
mock_state.set_state.assert_called_with("START")
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_suggest_router_media_group(self, mock_message, mock_state, mock_db):
|
|
||||||
"""Тест обработки медиагруппы"""
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_text_message') as mock_get_text:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.prepare_media_group_from_middlewares') as mock_prepare:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.send_media_group_message_to_private_chat') as mock_send_group:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.send_text_message') as mock_send_text:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
|
||||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
|
||||||
# Настройка моков для медиагруппы
|
|
||||||
mock_message.media_group_id = 'group_123'
|
|
||||||
mock_message.content_type = 'photo'
|
|
||||||
|
|
||||||
# Создаем мок альбома
|
|
||||||
album = [mock_message]
|
|
||||||
album[0].caption = 'Подпись к медиагруппе'
|
|
||||||
|
|
||||||
mock_get_text.return_value = 'Обработанная подпись'
|
|
||||||
mock_keyboard_post.return_value = Mock()
|
|
||||||
mock_keyboard.return_value = Mock()
|
|
||||||
mock_prepare.return_value = ['media1', 'media2']
|
|
||||||
mock_send_group.return_value = 123
|
|
||||||
mock_send_text.return_value = 456
|
|
||||||
mock_messages.return_value = "Медиагруппа отправлена!"
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await suggest_router(mock_message, mock_state, album)
|
|
||||||
|
|
||||||
# Проверки
|
|
||||||
mock_get_text.assert_called_once()
|
|
||||||
mock_prepare.assert_called_once()
|
|
||||||
mock_send_group.assert_called_once()
|
|
||||||
# Проверяем, что send_text_message был вызван (может быть вызван несколько раз)
|
|
||||||
assert mock_send_text.call_count >= 1
|
|
||||||
mock_db.update_helper_message_in_db.assert_called_once()
|
|
||||||
mock_message.answer.assert_called_once()
|
|
||||||
mock_state.set_state.assert_called_with("START")
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_suggest_router_unsupported_content(self, mock_message, mock_state):
|
|
||||||
"""Тест обработки неподдерживаемого типа контента"""
|
|
||||||
# Настройка моков для неподдерживаемого контента
|
|
||||||
mock_message.content_type = 'document'
|
|
||||||
mock_message.media_group_id = None
|
|
||||||
|
|
||||||
# Выполнение теста
|
|
||||||
await suggest_router(mock_message, mock_state)
|
|
||||||
|
|
||||||
# Проверяем, что отправлено сообщение о неподдерживаемом типе
|
|
||||||
mock_message.bot.send_message.assert_called_once()
|
|
||||||
call_args = mock_message.bot.send_message.call_args
|
|
||||||
# Проверяем текст сообщения (может быть в позиционных или именованных аргументах)
|
|
||||||
text = call_args.kwargs.get('text', '') or (call_args[0][1] if len(call_args[0]) > 1 else '')
|
|
||||||
assert 'не умею работать с таким сообщением' in text
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
pytest.main([__file__, '-v'])
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
[Telegram]
|
|
||||||
bot_token = test_token_123
|
|
||||||
preview_link = false
|
|
||||||
main_public = @test
|
|
||||||
group_for_posts = -1001234567890
|
|
||||||
group_for_message = -1001234567891
|
|
||||||
group_for_logs = -1001234567893
|
|
||||||
important_logs = -1001234567894
|
|
||||||
test_channel = -1001234567895
|
|
||||||
|
|
||||||
[Settings]
|
|
||||||
logs = true
|
|
||||||
test = false
|
|
||||||
@@ -1,208 +0,0 @@
|
|||||||
import pytest
|
|
||||||
from unittest.mock import Mock, patch
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
from helper_bot.utils.helper_func import (
|
|
||||||
get_first_name,
|
|
||||||
get_text_message,
|
|
||||||
check_username_and_full_name
|
|
||||||
)
|
|
||||||
from helper_bot.utils.messages import get_message
|
|
||||||
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory, get_global_instance
|
|
||||||
from database.db import BotDB
|
|
||||||
|
|
||||||
|
|
||||||
class TestHelperFunctions:
|
|
||||||
"""Тесты для вспомогательных функций"""
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_message(self):
|
|
||||||
"""Создает мок сообщения для тестирования"""
|
|
||||||
message = Mock()
|
|
||||||
message.from_user = Mock()
|
|
||||||
message.from_user.first_name = "Test"
|
|
||||||
message.from_user.full_name = "Test User"
|
|
||||||
message.from_user.username = "testuser"
|
|
||||||
return message
|
|
||||||
|
|
||||||
def test_get_first_name(self, mock_message):
|
|
||||||
"""Тест функции получения имени пользователя"""
|
|
||||||
# Тест с обычным именем
|
|
||||||
result = get_first_name(mock_message)
|
|
||||||
assert result == "Test"
|
|
||||||
|
|
||||||
# Тест с пустым именем - функция get_first_name не обрабатывает None
|
|
||||||
# поэтому этот тест будет падать, что ожидаемо
|
|
||||||
mock_message.from_user.first_name = None
|
|
||||||
try:
|
|
||||||
result = get_first_name(mock_message)
|
|
||||||
assert False, "Ожидалась ошибка при None first_name"
|
|
||||||
except AttributeError:
|
|
||||||
pass # Ожидаемое поведение
|
|
||||||
|
|
||||||
def test_get_text_message(self, mock_message):
|
|
||||||
"""Тест функции обработки текста сообщения"""
|
|
||||||
# Тест с обычным текстом
|
|
||||||
text = "Привет, это тестовое сообщение"
|
|
||||||
result = get_text_message(text, "Test", "testuser")
|
|
||||||
assert "Test" in result
|
|
||||||
assert "testuser" in result
|
|
||||||
assert "тестовое сообщение" in result
|
|
||||||
|
|
||||||
# Тест с пустым текстом
|
|
||||||
result = get_text_message("", "Test", "testuser")
|
|
||||||
assert "Test" in result
|
|
||||||
assert "testuser" in result
|
|
||||||
|
|
||||||
# Тест с текстом без специальных слов
|
|
||||||
text = "Обычный текст без специальных слов"
|
|
||||||
result = get_text_message(text, "Test", "testuser")
|
|
||||||
assert "Test" in result
|
|
||||||
assert "testuser" in result
|
|
||||||
assert "Обычный текст без специальных слов" in result
|
|
||||||
|
|
||||||
def test_check_username_and_full_name(self):
|
|
||||||
"""Тест функции проверки изменений username и full_name"""
|
|
||||||
# Создаем мок базы данных
|
|
||||||
mock_db = Mock(spec=BotDB)
|
|
||||||
mock_db.get_username_and_full_name = Mock(return_value=("olduser", "Old User"))
|
|
||||||
|
|
||||||
# Тест с измененными данными
|
|
||||||
result = check_username_and_full_name(123456, "newuser", "New User", mock_db)
|
|
||||||
assert result is True
|
|
||||||
|
|
||||||
# Тест с неизмененными данными
|
|
||||||
result = check_username_and_full_name(123456, "olduser", "Old User", mock_db)
|
|
||||||
assert result is False
|
|
||||||
|
|
||||||
# Тест с частично измененными данными
|
|
||||||
result = check_username_and_full_name(123456, "olduser", "New User", mock_db)
|
|
||||||
assert result is True
|
|
||||||
|
|
||||||
result = check_username_and_full_name(123456, "newuser", "Old User", mock_db)
|
|
||||||
assert result is True
|
|
||||||
|
|
||||||
|
|
||||||
class TestMessages:
|
|
||||||
"""Тесты для системы сообщений"""
|
|
||||||
|
|
||||||
def test_get_message(self):
|
|
||||||
"""Тест функции получения сообщений"""
|
|
||||||
# Тест с существующим ключом
|
|
||||||
result = get_message("Test", "HELLO_MESSAGE")
|
|
||||||
assert isinstance(result, str)
|
|
||||||
assert len(result) > 0
|
|
||||||
|
|
||||||
# Тест с несуществующим ключом
|
|
||||||
try:
|
|
||||||
result = get_message("Test", "NON_EXISTENT_KEY")
|
|
||||||
assert False, "Ожидалась ошибка KeyError"
|
|
||||||
except KeyError:
|
|
||||||
pass # Ожидаемое поведение
|
|
||||||
|
|
||||||
# Тест с пустым именем
|
|
||||||
result = get_message("", "HELLO_MESSAGE")
|
|
||||||
assert isinstance(result, str)
|
|
||||||
assert len(result) > 0
|
|
||||||
|
|
||||||
# Тест с None именем - ожидаем ошибку
|
|
||||||
try:
|
|
||||||
result = get_message(None, "HELLO_MESSAGE")
|
|
||||||
assert False, "Ожидалась ошибка TypeError"
|
|
||||||
except TypeError:
|
|
||||||
pass # Ожидаемое поведение
|
|
||||||
|
|
||||||
def test_get_message_all_types(self):
|
|
||||||
"""Тест всех типов сообщений"""
|
|
||||||
message_types = [
|
|
||||||
"HELLO_MESSAGE",
|
|
||||||
"SUGGEST_NEWS",
|
|
||||||
"SUGGEST_NEWS_2",
|
|
||||||
"BYE_MESSAGE",
|
|
||||||
"SUCCESS_SEND_MESSAGE",
|
|
||||||
"CONNECT_WITH_ADMIN",
|
|
||||||
"QUESTION"
|
|
||||||
]
|
|
||||||
|
|
||||||
for msg_type in message_types:
|
|
||||||
result = get_message("Test", msg_type)
|
|
||||||
assert isinstance(result, str)
|
|
||||||
assert len(result) > 0
|
|
||||||
|
|
||||||
|
|
||||||
class TestBaseDependencyFactory:
|
|
||||||
"""Тесты для фабрики зависимостей"""
|
|
||||||
|
|
||||||
def test_singleton_pattern(self):
|
|
||||||
"""Тест паттерна синглтон"""
|
|
||||||
# Сбрасываем глобальный экземпляр
|
|
||||||
import helper_bot.utils.base_dependency_factory
|
|
||||||
helper_bot.utils.base_dependency_factory._global_instance = None
|
|
||||||
|
|
||||||
# Получаем два экземпляра
|
|
||||||
instance1 = get_global_instance()
|
|
||||||
instance2 = get_global_instance()
|
|
||||||
|
|
||||||
# Проверяем, что это один и тот же объект
|
|
||||||
assert instance1 is instance2
|
|
||||||
assert id(instance1) == id(instance2)
|
|
||||||
|
|
||||||
def test_factory_initialization_with_mock_config(self):
|
|
||||||
"""Тест инициализации фабрики с мок конфигурацией"""
|
|
||||||
# Этот тест пропускаем, так как сложно замокать ConfigParser
|
|
||||||
# в контексте уже загруженных модулей
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_get_settings_method(self):
|
|
||||||
"""Тест метода get_settings"""
|
|
||||||
# Этот тест пропускаем, так как сложно замокать ConfigParser
|
|
||||||
# в контексте уже загруженных модулей
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_get_db_method(self):
|
|
||||||
"""Тест метода get_db"""
|
|
||||||
with patch('helper_bot.utils.base_dependency_factory.configparser.ConfigParser'):
|
|
||||||
with patch('helper_bot.utils.base_dependency_factory.BotDB') as mock_db:
|
|
||||||
factory = BaseDependencyFactory()
|
|
||||||
db = factory.get_db()
|
|
||||||
|
|
||||||
assert db is not None
|
|
||||||
assert db == factory.database
|
|
||||||
|
|
||||||
|
|
||||||
class TestDatabaseIntegration:
|
|
||||||
"""Тесты интеграции с базой данных"""
|
|
||||||
|
|
||||||
def test_database_connection(self):
|
|
||||||
"""Тест подключения к базе данных"""
|
|
||||||
with patch('helper_bot.utils.base_dependency_factory.configparser.ConfigParser'):
|
|
||||||
with patch('helper_bot.utils.base_dependency_factory.BotDB') as mock_db:
|
|
||||||
factory = BaseDependencyFactory()
|
|
||||||
|
|
||||||
# Проверяем, что база данных была создана
|
|
||||||
mock_db.assert_called_once()
|
|
||||||
|
|
||||||
# Проверяем, что get_db возвращает тот же экземпляр
|
|
||||||
db1 = factory.get_db()
|
|
||||||
db2 = factory.get_db()
|
|
||||||
assert db1 is db2
|
|
||||||
|
|
||||||
|
|
||||||
class TestConfigurationHandling:
|
|
||||||
"""Тесты обработки конфигурации"""
|
|
||||||
|
|
||||||
def test_boolean_config_values(self):
|
|
||||||
"""Тест обработки булевых значений в конфигурации"""
|
|
||||||
# Этот тест пропускаем, так как сложно замокать ConfigParser
|
|
||||||
# в контексте уже загруженных модулей
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_string_config_values(self):
|
|
||||||
"""Тест обработки строковых значений в конфигурации"""
|
|
||||||
# Этот тест пропускаем, так как сложно замокать ConfigParser
|
|
||||||
# в контексте уже загруженных модулей
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
pytest.main([__file__, '-v'])
|
|
||||||
68
voice_bot/handlers/callback_handler.py
Normal file
68
voice_bot/handlers/callback_handler.py
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from aiogram import Router, F
|
||||||
|
from aiogram.types import CallbackQuery
|
||||||
|
|
||||||
|
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory
|
||||||
|
|
||||||
|
callback_router = Router()
|
||||||
|
|
||||||
|
bdf = BaseDependencyFactory()
|
||||||
|
|
||||||
|
GROUP_FOR_LOGS = bdf.settings['Telegram']['group_for_logs']
|
||||||
|
GROUP_FOR_POST = bdf.settings['Telegram']['group_for_posts']
|
||||||
|
IMPORTANT_LOGS = bdf.settings['Telegram']['important_logs']
|
||||||
|
PREVIEW_LINK = bdf.settings['Telegram']['preview_link']
|
||||||
|
LOGS = bdf.settings['Settings']['logs']
|
||||||
|
TEST = bdf.settings['Settings']['test']
|
||||||
|
|
||||||
|
BotDB = bdf.get_db()
|
||||||
|
|
||||||
|
|
||||||
|
@callback_router.callback_query(
|
||||||
|
F.data == "save"
|
||||||
|
)
|
||||||
|
async def save_voice_message(call: CallbackQuery):
|
||||||
|
file_name = ''
|
||||||
|
file_id = 1
|
||||||
|
user_id = BotDB.get_user_id_by_message_id_for_voice_bot(call.message.message_id)
|
||||||
|
# Проверяем что запись о файле есть в базе данных
|
||||||
|
is_having_audio_from_user = BotDB.get_last_user_audio_record(user_id=user_id)
|
||||||
|
if is_having_audio_from_user is False:
|
||||||
|
# Если нет, то генерируем имя файла
|
||||||
|
file_name = f'message_from_{user_id}_number_{file_id}'
|
||||||
|
else:
|
||||||
|
# Иначе берем последнюю запись из БД, добавляем к ней 1, и создаем новую запись
|
||||||
|
file_name = BotDB.get_path_for_audio_record(user_id=user_id)
|
||||||
|
file_id = BotDB.get_id_for_audio_record(user_id) + 1
|
||||||
|
path = Path(f'voice_users/{file_name}.ogg')
|
||||||
|
if path.exists():
|
||||||
|
file_name = f'message_from_{user_id}_number_{file_id}'
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
# Собираем инфо о сообщении
|
||||||
|
time_UTC = int(time.time())
|
||||||
|
date_added = datetime.fromtimestamp(time_UTC)
|
||||||
|
|
||||||
|
# Сохраняем в базку
|
||||||
|
BotDB.add_audio_record(file_name, user_id, date_added, 0, file_id)
|
||||||
|
|
||||||
|
file_info = await call.message.bot.get_file(file_id=call.message.voice.file_id)
|
||||||
|
downloaded_file = await call.message.bot.download_file(file_path=file_info.file_path)
|
||||||
|
with open(f'voice_users/{file_name}.ogg', 'wb') as new_file:
|
||||||
|
new_file.write(downloaded_file.read())
|
||||||
|
|
||||||
|
await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id)
|
||||||
|
await call.answer(text='Сохранено!', cache_time=3)
|
||||||
|
|
||||||
|
|
||||||
|
@callback_router.callback_query(
|
||||||
|
F.data == "delete"
|
||||||
|
)
|
||||||
|
async def delete_voice_message(call: CallbackQuery):
|
||||||
|
# Получаем из базы автора + отправляем сообщение + удаляем сообщение из предложки
|
||||||
|
|
||||||
|
await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id)
|
||||||
|
await call.answer(text='Удалено!', cache_time=3)
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import random
|
import random
|
||||||
import asyncio
|
import time
|
||||||
from datetime import datetime
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from aiogram import Router, types, F
|
from aiogram import Router, types, F
|
||||||
@@ -9,22 +8,26 @@ from aiogram.fsm.context import FSMContext
|
|||||||
from aiogram.types import FSInputFile
|
from aiogram.types import FSInputFile
|
||||||
|
|
||||||
from helper_bot.filters.main import ChatTypeFilter
|
from helper_bot.filters.main import ChatTypeFilter
|
||||||
from helper_bot.utils.base_dependency_factory import get_global_instance
|
from helper_bot.middlewares.blacklist_middleware import BlacklistMiddleware
|
||||||
|
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory
|
||||||
|
from helper_bot.utils.helper_func import update_user_info, check_user_emoji, send_voice_message
|
||||||
from logs.custom_logger import logger
|
from logs.custom_logger import logger
|
||||||
from voice_bot.keyboards.keyboards import get_main_keyboard
|
from voice_bot.keyboards.keyboards import get_main_keyboard, get_reply_keyboard_for_voice
|
||||||
from voice_bot.utils.helper_func import last_message
|
from voice_bot.utils.helper_func import last_message
|
||||||
|
|
||||||
voice_router = Router()
|
voice_router = Router()
|
||||||
|
|
||||||
bdf = get_global_instance()
|
bdf = BaseDependencyFactory()
|
||||||
|
|
||||||
GROUP_FOR_LOGS = bdf.settings['Telegram']['group_for_logs']
|
GROUP_FOR_LOGS = bdf.settings['Telegram']['group_for_logs']
|
||||||
|
GROUP_FOR_POST = bdf.settings['Telegram']['group_for_posts']
|
||||||
IMPORTANT_LOGS = bdf.settings['Telegram']['important_logs']
|
IMPORTANT_LOGS = bdf.settings['Telegram']['important_logs']
|
||||||
PREVIEW_LINK = bdf.settings['Telegram']['preview_link']
|
PREVIEW_LINK = bdf.settings['Telegram']['preview_link']
|
||||||
LOGS = bdf.settings['Settings']['logs']
|
LOGS = bdf.settings['Settings']['logs']
|
||||||
TEST = bdf.settings['Settings']['test']
|
TEST = bdf.settings['Settings']['test']
|
||||||
|
|
||||||
BotDB = bdf.get_db()
|
BotDB = bdf.get_db()
|
||||||
|
voice_router.message.middleware(BlacklistMiddleware())
|
||||||
|
|
||||||
|
|
||||||
@voice_router.message(
|
@voice_router.message(
|
||||||
@@ -33,21 +36,36 @@ BotDB = bdf.get_db()
|
|||||||
)
|
)
|
||||||
async def restart_function(message: types.Message, state: FSMContext):
|
async def restart_function(message: types.Message, state: FSMContext):
|
||||||
await message.forward(chat_id=GROUP_FOR_LOGS)
|
await message.forward(chat_id=GROUP_FOR_LOGS)
|
||||||
|
await update_user_info('voice', message)
|
||||||
|
check_user_emoji(message)
|
||||||
markup = get_main_keyboard()
|
markup = get_main_keyboard()
|
||||||
await message.answer(text='Я перезапущен!',
|
await message.answer(text='Я перезапущен!',
|
||||||
reply_markup=markup)
|
reply_markup=markup)
|
||||||
await state.set_state('START')
|
await state.set_state('START')
|
||||||
|
|
||||||
|
|
||||||
|
@voice_router.message(
|
||||||
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
|
Command("emoji")
|
||||||
|
)
|
||||||
|
async def handle_emoji_message(message: types.Message, state: FSMContext):
|
||||||
|
await message.forward(chat_id=GROUP_FOR_LOGS)
|
||||||
|
user_emoji = check_user_emoji(message)
|
||||||
|
await state.set_state("START")
|
||||||
|
if user_emoji is not None:
|
||||||
|
await message.answer(f'Твоя эмодзя - {user_emoji}', parse_mode='HTML')
|
||||||
|
|
||||||
|
|
||||||
@voice_router.message(
|
@voice_router.message(
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
Command("help")
|
Command("help")
|
||||||
)
|
)
|
||||||
async def help_function(message: types.Message, state: FSMContext):
|
async def help_function(message: types.Message, state: FSMContext):
|
||||||
await message.forward(chat_id=GROUP_FOR_LOGS)
|
await message.forward(chat_id=GROUP_FOR_LOGS)
|
||||||
|
await update_user_info('voice', message)
|
||||||
await message.answer(
|
await message.answer(
|
||||||
text='Скорее всего ответы на твои вопросы есть здесь, ознакомься: https://telegra.ph/Instrukciya-k-botu-Golosa-Bijsk-10-11-2'
|
text='Скорее всего ответы на твои вопросы есть здесь, ознакомься: https://telegra.ph/Instrukciya-k-botu-Golosa-Bijsk-10-11-2'
|
||||||
'\nЕсли это не поможет, пиши в тг: @Kerrad1', disable_web_page_preview=not PREVIEW_LINK)
|
'\nЕсли это не поможет, пиши в личку: @Kerrad1', disable_web_page_preview=not PREVIEW_LINK)
|
||||||
await state.set_state('START')
|
await state.set_state('START')
|
||||||
|
|
||||||
|
|
||||||
@@ -58,55 +76,78 @@ async def help_function(message: types.Message, state: FSMContext):
|
|||||||
async def start(message: types.Message, state: FSMContext):
|
async def start(message: types.Message, state: FSMContext):
|
||||||
await state.set_state("START")
|
await state.set_state("START")
|
||||||
await message.forward(chat_id=GROUP_FOR_LOGS)
|
await message.forward(chat_id=GROUP_FOR_LOGS)
|
||||||
|
await update_user_info('voice', message)
|
||||||
|
user_emoji = check_user_emoji(message)
|
||||||
try:
|
try:
|
||||||
name_stick_hello = list(Path('Stick').rglob('Hello_*'))
|
name_stick_hello = list(Path('Stick').rglob('Hello_*'))
|
||||||
random_stick_hello = random.choice(name_stick_hello)
|
random_stick_hello = random.choice(name_stick_hello)
|
||||||
random_stick_hello = FSInputFile(path=random_stick_hello)
|
random_stick_hello = FSInputFile(path=random_stick_hello)
|
||||||
logger.info(f"Стикер успешно получен из БД. Наименование стикера: {name_stick_hello}")
|
logger.info(f"Стикер успешно получен из БД. Наименование стикера: {name_stick_hello}")
|
||||||
await message.answer_sticker(random_stick_hello)
|
await message.answer_sticker(random_stick_hello)
|
||||||
await asyncio.sleep(0.3)
|
time.sleep(0.3)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if LOGS:
|
if LOGS:
|
||||||
await message.bot.send_message(IMPORTANT_LOGS, f'Отправка приветственных стикеров лажает. Ошибка: {e}')
|
await message.bot.send_message(IMPORTANT_LOGS, f'Отправка приветственных стикеров лажает. Ошибка: {e}')
|
||||||
markup = get_main_keyboard()
|
markup = get_main_keyboard()
|
||||||
await message.answer(text="<b>Привет.</b>", parse_mode='html', reply_markup=markup,
|
await message.answer(text="<b>Привет.</b>", parse_mode='html', reply_markup=markup,
|
||||||
disable_web_page_preview=not PREVIEW_LINK)
|
disable_web_page_preview=not PREVIEW_LINK)
|
||||||
await asyncio.sleep(0.3)
|
time.sleep(0.3)
|
||||||
await message.answer(text="<i>Здесь можно послушать голосовые сообщения от совершенно незнакомых людей из "
|
await message.answer(text="<i>Здесь можно послушать голосовые сообщения от совершенно незнакомых людей из "
|
||||||
"Бийска</i>",
|
"Бийска</i>",
|
||||||
parse_mode='html', reply_markup=markup,
|
parse_mode='html', reply_markup=markup,
|
||||||
disable_web_page_preview=not PREVIEW_LINK)
|
disable_web_page_preview=not PREVIEW_LINK)
|
||||||
await asyncio.sleep(1)
|
time.sleep(1)
|
||||||
await message.answer(text="Это почти как написать письмо, положить его в бутылку и швырнуть в океан. Никогда не "
|
await message.answer(text="Это почти как написать письмо, положить его в бутылку и швырнуть в океан. Никогда не "
|
||||||
"узнаешь, послушал его кто-то или нет и ответить тоже не получится..",
|
"узнаешь, послушал его кто-то или нет и ответить тоже не получится..",
|
||||||
parse_mode='html', reply_markup=markup,
|
parse_mode='html', reply_markup=markup,
|
||||||
disable_web_page_preview=not PREVIEW_LINK)
|
disable_web_page_preview=not PREVIEW_LINK)
|
||||||
await asyncio.sleep(0.8)
|
time.sleep(0.8)
|
||||||
await message.answer(text="Записывать можно всё что угодно — никаких правил нет. Главное — твой голос, <i>хотя "
|
await message.answer(text="Записывать можно всё что угодно — никаких правил нет. Главное — твой голос, <i>хотя "
|
||||||
"бы на 5-10 секунд</i>",
|
"бы на 5-10 секунд</i>",
|
||||||
parse_mode='html', reply_markup=markup,
|
parse_mode='html', reply_markup=markup,
|
||||||
disable_web_page_preview=not PREVIEW_LINK)
|
disable_web_page_preview=not PREVIEW_LINK)
|
||||||
await asyncio.sleep(1.5)
|
time.sleep(1.5)
|
||||||
await message.answer(text="Здесь всё анонимно: тот, кому я отправлю твое сообщение, не узнает ни твое имя, "
|
await message.answer(text="Здесь всё анонимно: тот, кому я отправлю твое сообщение, не узнает ни твое имя, "
|
||||||
"ни твой аккаунт (так что можно не стесняться говорить то, что не стал(а) бы "
|
"ни твой аккаунт (так что можно не стесняться говорить то, что не стал(а) бы "
|
||||||
"выкладывать в собственные соцсети)",
|
"выкладывать в собственные соцсети)",
|
||||||
parse_mode='html', reply_markup=markup,
|
parse_mode='html', reply_markup=markup,
|
||||||
disable_web_page_preview=not PREVIEW_LINK)
|
disable_web_page_preview=not PREVIEW_LINK)
|
||||||
await asyncio.sleep(1.3)
|
time.sleep(1.3)
|
||||||
await message.answer(text="Если не знаешь, что сказать, можешь просто прочитать любое текстовое сообщение из "
|
await message.answer(text="Если не знаешь, что сказать, можешь просто прочитать любое текстовое сообщение из "
|
||||||
"недавно полученных или отправленных (или спеть, рассказать стихотворенье)",
|
"недавно полученных или отправленных (или спеть, рассказать стихотворенье)",
|
||||||
parse_mode='html', reply_markup=markup,
|
parse_mode='html', reply_markup=markup,
|
||||||
disable_web_page_preview=not PREVIEW_LINK)
|
disable_web_page_preview=not PREVIEW_LINK)
|
||||||
await asyncio.sleep(0.8)
|
time.sleep(0.8)
|
||||||
|
await message.answer(text=f"Любые войсы будут помечены эмоджи. <b>Твой эмоджи - </b>{user_emoji}"
|
||||||
|
f"Таким эмоджи будут помечены твои сообщения для других "
|
||||||
|
f"Но другие люди не узнают кто за каким эмоджи скрывается:)",
|
||||||
|
parse_mode='html', reply_markup=markup,
|
||||||
|
disable_web_page_preview=not PREVIEW_LINK)
|
||||||
|
time.sleep(0.8)
|
||||||
await message.answer(text="Так же можешь ознакомиться с инструкцией к боту по команде /help",
|
await message.answer(text="Так же можешь ознакомиться с инструкцией к боту по команде /help",
|
||||||
parse_mode='html', reply_markup=markup,
|
parse_mode='html', reply_markup=markup,
|
||||||
disable_web_page_preview=not PREVIEW_LINK)
|
disable_web_page_preview=not PREVIEW_LINK)
|
||||||
await asyncio.sleep(0.8)
|
time.sleep(0.8)
|
||||||
await message.answer(text="<b>ну всё, достаточно инструкций. записывайся! Микрофон твой - </b> 🎤",
|
await message.answer(text="<b>Ну всё, достаточно инструкций. записывайся! Микрофон твой - </b> 🎤",
|
||||||
parse_mode='html', reply_markup=markup,
|
parse_mode='html', reply_markup=markup,
|
||||||
disable_web_page_preview=not PREVIEW_LINK)
|
disable_web_page_preview=not PREVIEW_LINK)
|
||||||
|
|
||||||
|
|
||||||
|
@voice_router.message(
|
||||||
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
|
Command("refresh")
|
||||||
|
)
|
||||||
|
async def refresh_listen_function(message: types.Message, state: FSMContext):
|
||||||
|
await message.forward(chat_id=GROUP_FOR_LOGS)
|
||||||
|
await update_user_info('voice', message)
|
||||||
|
markup = get_main_keyboard()
|
||||||
|
BotDB.delete_listen_count_for_user(message.from_user.id)
|
||||||
|
await message.answer(
|
||||||
|
text='Прослушивания очищены. Можешь начать слушать заново🤗', disable_web_page_preview=not PREVIEW_LINK,
|
||||||
|
markup=markup)
|
||||||
|
await state.set_state('START')
|
||||||
|
|
||||||
|
|
||||||
@voice_router.message(
|
@voice_router.message(
|
||||||
StateFilter("START"),
|
StateFilter("START"),
|
||||||
ChatTypeFilter(chat_type=["private"]),
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
@@ -116,53 +157,33 @@ async def standup_write(message: types.Message, state: FSMContext):
|
|||||||
await message.forward(chat_id=GROUP_FOR_LOGS)
|
await message.forward(chat_id=GROUP_FOR_LOGS)
|
||||||
markup = types.ReplyKeyboardRemove()
|
markup = types.ReplyKeyboardRemove()
|
||||||
await message.answer(text='Хорошо, теперь пришли мне свое голосовое сообщение', reply_markup=markup)
|
await message.answer(text='Хорошо, теперь пришли мне свое голосовое сообщение', reply_markup=markup)
|
||||||
message_with_date = last_message()
|
try:
|
||||||
await message.answer(text=message_with_date, parse_mode="html")
|
message_with_date = last_message()
|
||||||
|
await message.answer(text=message_with_date, parse_mode="html")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f'Не удалось получить дату последнего сообщения - {e}')
|
||||||
await state.set_state('STANDUP_WRITE')
|
await state.set_state('STANDUP_WRITE')
|
||||||
|
|
||||||
|
|
||||||
@voice_router.message(
|
@voice_router.message(
|
||||||
StateFilter("STANDUP_WRITE"),
|
StateFilter("STANDUP_WRITE"),
|
||||||
ChatTypeFilter(chat_type=["private"])
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
)
|
)
|
||||||
async def save_voice_message(message: types.Message, state: FSMContext):
|
async def suggest_voice(message: types.Message, state: FSMContext):
|
||||||
|
logger.info(
|
||||||
|
f"Вызов функции suggest_voice. Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}")
|
||||||
|
await message.forward(chat_id=GROUP_FOR_LOGS)
|
||||||
markup = get_main_keyboard()
|
markup = get_main_keyboard()
|
||||||
if message.content_type == 'voice':
|
if message.content_type == 'voice':
|
||||||
await message.forward(chat_id=GROUP_FOR_LOGS)
|
markup_for_voice = get_reply_keyboard_for_voice()
|
||||||
file_name = ''
|
# Отправляем аудио в приватный канал
|
||||||
file_id = 1
|
sent_message = await send_voice_message(GROUP_FOR_POST, message,
|
||||||
# Проверяем что запись о файле есть в базе данных
|
message.voice.file_id, markup_for_voice)
|
||||||
is_having_audio_from_user = BotDB.get_last_user_audio_record(user_id=message.from_user.id)
|
|
||||||
if is_having_audio_from_user is False:
|
|
||||||
# Если нет, то генерируем имя файла
|
|
||||||
file_name = f'message_from_{message.from_user.id}_number_{file_id}'
|
|
||||||
else:
|
|
||||||
# Иначе берем последнюю запись из БД, добавляем к ней 1, и создаем новую запись
|
|
||||||
file_name = BotDB.get_path_for_audio_record(user_id=message.from_user.id)
|
|
||||||
file_id = BotDB.get_id_for_audio_record(message.from_user.id) + 1
|
|
||||||
path = Path(f'voice_users/{file_name}.ogg')
|
|
||||||
if path.exists():
|
|
||||||
file_name = f'message_from_{message.from_user.id}_number_{file_id}'
|
|
||||||
else:
|
|
||||||
pass
|
|
||||||
# Собираем инфо о сообщении
|
|
||||||
author_id = message.from_user.id
|
|
||||||
time_UTC = int(datetime.now().timestamp())
|
|
||||||
date_added = datetime.fromtimestamp(time_UTC)
|
|
||||||
|
|
||||||
# Сохраняем в базку
|
# Сохраняем в базу инфо о посте
|
||||||
BotDB.add_audio_record(file_name, author_id, date_added, 0, file_id)
|
BotDB.set_user_id_and_message_id_for_voice_bot(sent_message.message_id, message.from_user.id)
|
||||||
|
|
||||||
# Сохраняем файл на сервер
|
# Отправляем юзеру ответ и возвращаем его в меню
|
||||||
# file_info = message.bot.get_file(file_id=message.voice.file_id)
|
|
||||||
# downloaded_file = message.bot.download_file(file_path=file_info.file_path)
|
|
||||||
# with open(f'voice_users/{file_name}.ogg', 'wb') as new_file:
|
|
||||||
# new_file.write(downloaded_file)
|
|
||||||
|
|
||||||
file_info = await message.bot.get_file(file_id=message.voice.file_id)
|
|
||||||
downloaded_file = await message.bot.download_file(file_path=file_info.file_path)
|
|
||||||
with open(f'voice_users/{file_name}.ogg', 'wb') as new_file:
|
|
||||||
new_file.write(downloaded_file.read())
|
|
||||||
await message.answer(text='Окей, сохранил!👌', reply_markup=markup)
|
await message.answer(text='Окей, сохранил!👌', reply_markup=markup)
|
||||||
await state.set_state('START')
|
await state.set_state('START')
|
||||||
else:
|
else:
|
||||||
@@ -177,24 +198,38 @@ async def save_voice_message(message: types.Message, state: FSMContext):
|
|||||||
ChatTypeFilter(chat_type=["private"]),
|
ChatTypeFilter(chat_type=["private"]),
|
||||||
F.text == '🎧Послушать'
|
F.text == '🎧Послушать'
|
||||||
)
|
)
|
||||||
async def standup_listen_audio(message: types.Message, state: FSMContext):
|
async def standup_listen_audio(message: types.Message):
|
||||||
check_audio = BotDB.check_listen_audio(user_id=message.from_user.id)
|
check_audio = BotDB.check_listen_audio(user_id=message.from_user.id)
|
||||||
list_audio = list(check_audio)
|
list_audio = list(check_audio)
|
||||||
markup = get_main_keyboard()
|
markup = get_main_keyboard()
|
||||||
await message.forward(chat_id=GROUP_FOR_LOGS)
|
|
||||||
if not list_audio:
|
if not list_audio:
|
||||||
await message.answer(text='Прости, ты прослушал все аудио😔. Возвращайся позже, возможно наша база пополнится',
|
await message.answer(text='Прости, ты прослушал все аудио😔. Возвращайся позже, возможно наша база пополнится',
|
||||||
reply_markup=markup)
|
reply_markup=markup)
|
||||||
message_with_date = last_message()
|
try:
|
||||||
message.send_message(chat_id=message.chat.id, text=message_with_date, parse_mode="html")
|
message_with_date = last_message()
|
||||||
|
await message.answer(text=message_with_date, parse_mode="html")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f'Не удалось получить последнюю дату {e}')
|
||||||
else:
|
else:
|
||||||
|
# Получаем ссылку на аудио сообщение пользователя
|
||||||
number_element = random.randint(0, len(list_audio) - 1)
|
number_element = random.randint(0, len(list_audio) - 1)
|
||||||
audio_for_user = check_audio[number_element]
|
audio_for_user = check_audio[number_element]
|
||||||
|
|
||||||
|
# Получаем автора записи + эмодзи по нему
|
||||||
|
user_id = BotDB.get_user_id_by_file_name(audio_for_user)
|
||||||
|
date_added = BotDB.get_date_by_file_name(audio_for_user)
|
||||||
|
user_emoji = BotDB.check_emoji_for_user(user_id)
|
||||||
|
|
||||||
path = Path(f'voice_users/{audio_for_user}.ogg')
|
path = Path(f'voice_users/{audio_for_user}.ogg')
|
||||||
# voice = open(path, 'rb')
|
|
||||||
voice = FSInputFile(path)
|
voice = FSInputFile(path)
|
||||||
|
|
||||||
# Маркируем сообщение как прослушанное
|
# Маркируем сообщение как прослушанное
|
||||||
BotDB.mark_listened_audio(audio_for_user, user_id=message.from_user.id)
|
BotDB.mark_listened_audio(audio_for_user, user_id=message.from_user.id)
|
||||||
await message.bot.send_voice(message.chat.id, voice=voice, reply_markup=markup)
|
|
||||||
await message.forward(chat_id=GROUP_FOR_LOGS)
|
# Формируем подпись
|
||||||
await state.set_state('START')
|
if user_emoji:
|
||||||
|
caption = f'{user_emoji}\nДата записи: {date_added}'
|
||||||
|
else:
|
||||||
|
caption = f'Дата записи: {date_added}'
|
||||||
|
await message.bot.send_voice(chat_id=message.chat.id, voice=voice, caption=caption, reply_markup=markup)
|
||||||
|
await message.answer(text=f'Осталось непрослушанных: <b>{len(check_audio) - 1}</b>', reply_markup=markup)
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
from aiogram import types
|
from aiogram import types
|
||||||
from aiogram.utils.keyboard import ReplyKeyboardBuilder
|
from aiogram.utils.keyboard import ReplyKeyboardBuilder, InlineKeyboardBuilder
|
||||||
|
|
||||||
|
|
||||||
def get_main_keyboard():
|
def get_main_keyboard():
|
||||||
@@ -8,3 +8,15 @@ def get_main_keyboard():
|
|||||||
builder.add(types.KeyboardButton(text="🎧Послушать"))
|
builder.add(types.KeyboardButton(text="🎧Послушать"))
|
||||||
markup = builder.as_markup(resize_keyboard=True)
|
markup = builder.as_markup(resize_keyboard=True)
|
||||||
return markup
|
return markup
|
||||||
|
|
||||||
|
|
||||||
|
def get_reply_keyboard_for_voice():
|
||||||
|
builder = InlineKeyboardBuilder()
|
||||||
|
builder.row(types.InlineKeyboardButton(
|
||||||
|
text="Сохранить", callback_data="save")
|
||||||
|
)
|
||||||
|
builder.row(types.InlineKeyboardButton(
|
||||||
|
text="Удалить", callback_data="delete")
|
||||||
|
)
|
||||||
|
markup = builder.as_markup(resize_keyboard=True, one_time_keyboard=True)
|
||||||
|
return markup
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ from aiogram.client.default import DefaultBotProperties
|
|||||||
from aiogram.fsm.storage.memory import MemoryStorage
|
from aiogram.fsm.storage.memory import MemoryStorage
|
||||||
from aiogram.fsm.strategy import FSMStrategy
|
from aiogram.fsm.strategy import FSMStrategy
|
||||||
|
|
||||||
from voice_bot.voice_handler.voice_handler import voice_router
|
from voice_bot.handlers.callback_handler import callback_router
|
||||||
|
from voice_bot.handlers.voice_handler import voice_router
|
||||||
|
|
||||||
|
|
||||||
async def start_bot(bdf):
|
async def start_bot(bdf):
|
||||||
@@ -13,6 +14,6 @@ async def start_bot(bdf):
|
|||||||
link_preview_is_disabled=bdf.settings['Telegram']['preview_link']
|
link_preview_is_disabled=bdf.settings['Telegram']['preview_link']
|
||||||
))
|
))
|
||||||
dp = Dispatcher(storage=MemoryStorage(), fsm_strategy=FSMStrategy.GLOBAL_USER)
|
dp = Dispatcher(storage=MemoryStorage(), fsm_strategy=FSMStrategy.GLOBAL_USER)
|
||||||
dp.include_routers(voice_router)
|
dp.include_routers(voice_router, callback_router)
|
||||||
await bot.delete_webhook(drop_pending_updates=True)
|
await bot.delete_webhook(drop_pending_updates=True)
|
||||||
await dp.start_polling(bot, skip_updates=True)
|
await dp.start_polling(bot, skip_updates=True)
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import time
|
import time
|
||||||
import html
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from helper_bot.utils.base_dependency_factory import get_global_instance
|
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory
|
||||||
|
|
||||||
bdf = get_global_instance()
|
bdf = BaseDependencyFactory()
|
||||||
|
|
||||||
BotDB = bdf.get_db()
|
BotDB = bdf.get_db()
|
||||||
|
|
||||||
@@ -12,6 +11,9 @@ BotDB = bdf.get_db()
|
|||||||
def last_message():
|
def last_message():
|
||||||
# функция с отображением сообщения "Последнее сообщение было записано"
|
# функция с отображением сообщения "Последнее сообщение было записано"
|
||||||
date_from_db = BotDB.last_date_audio()
|
date_from_db = BotDB.last_date_audio()
|
||||||
|
if date_from_db is None:
|
||||||
|
return None
|
||||||
|
|
||||||
parse_date = datetime.strptime(date_from_db, "%Y-%m-%d %H:%M:%S")
|
parse_date = datetime.strptime(date_from_db, "%Y-%m-%d %H:%M:%S")
|
||||||
last_voice_time_timestamp = time.mktime(parse_date.timetuple())
|
last_voice_time_timestamp = time.mktime(parse_date.timetuple())
|
||||||
time_now_timestamp = time.time()
|
time_now_timestamp = time.time()
|
||||||
@@ -23,19 +25,13 @@ def last_message():
|
|||||||
message_with_date = ''
|
message_with_date = ''
|
||||||
if much_minutes_ago <= 60:
|
if much_minutes_ago <= 60:
|
||||||
word_minute = plural_time(1, much_minutes_ago)
|
word_minute = plural_time(1, much_minutes_ago)
|
||||||
# Экранируем потенциально проблемные символы
|
message_with_date = f'<b>Последнее сообщение было записано {word_minute} назад</b>'
|
||||||
word_minute_escaped = html.escape(word_minute)
|
|
||||||
message_with_date = f'<b>Последнее сообщение было записано {word_minute_escaped} назад</b>'
|
|
||||||
elif much_minutes_ago > 60 and much_hour_ago <= 24:
|
elif much_minutes_ago > 60 and much_hour_ago <= 24:
|
||||||
word_hour = plural_time(2, much_hour_ago)
|
word_hour = plural_time(2, much_hour_ago)
|
||||||
# Экранируем потенциально проблемные символы
|
message_with_date = f'<b>Последнее сообщение было записано {word_hour} назад</b>'
|
||||||
word_hour_escaped = html.escape(word_hour)
|
|
||||||
message_with_date = f'<b>Последнее сообщение было записано {word_hour_escaped} назад</b>'
|
|
||||||
elif much_hour_ago > 24:
|
elif much_hour_ago > 24:
|
||||||
word_day = plural_time(3, much_days_ago)
|
word_day = plural_time(3, much_days_ago)
|
||||||
# Экранируем потенциально проблемные символы
|
message_with_date = f'<b>Последнее сообщение было записано {word_day} назад</b>'
|
||||||
word_day_escaped = html.escape(word_day)
|
|
||||||
message_with_date = f'<b>Последнее сообщение было записано {word_day_escaped} назад</b>'
|
|
||||||
return message_with_date
|
return message_with_date
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from helper_bot.utils.base_dependency_factory import get_global_instance
|
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory
|
||||||
from voice_bot.main import start_bot
|
from voice_bot.main import start_bot
|
||||||
|
|
||||||
bdf = get_global_instance()
|
bdf = BaseDependencyFactory()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
asyncio.run(start_bot(get_global_instance()))
|
asyncio.run(start_bot(BaseDependencyFactory()))
|
||||||
|
|||||||
Reference in New Issue
Block a user