import fix

This commit is contained in:
2026-02-01 22:49:25 +03:00
parent bba5550e15
commit 731e68a597
100 changed files with 490 additions and 605 deletions

View File

@@ -9,13 +9,12 @@
- async_db: основной класс AsyncBotDB - async_db: основной класс AsyncBotDB
""" """
from .models import (
User, BlacklistUser, UserMessage, TelegramPost, PostContent,
MessageContentLink, Admin, Migration, AudioMessage, AudioListenRecord, AudioModerate
)
from .repository_factory import RepositoryFactory
from .base import DatabaseConnection
from .async_db import AsyncBotDB from .async_db import AsyncBotDB
from .base import DatabaseConnection
from .models import (Admin, AudioListenRecord, AudioMessage, AudioModerate,
BlacklistUser, MessageContentLink, Migration, PostContent,
TelegramPost, User, UserMessage)
from .repository_factory import RepositoryFactory
# Для обратной совместимости экспортируем старый интерфейс # Для обратной совместимости экспортируем старый интерфейс
__all__ = [ __all__ = [

View File

@@ -1,11 +1,11 @@
import aiosqlite
from datetime import datetime from datetime import datetime
from typing import Optional, List, Dict, Any, Tuple from typing import Any, Dict, List, Optional, Tuple
import aiosqlite
from database.models import (Admin, AudioMessage, BlacklistHistoryRecord,
BlacklistUser, PostContent, TelegramPost, User,
UserMessage)
from database.repository_factory import RepositoryFactory from database.repository_factory import RepositoryFactory
from database.models import (
User, BlacklistUser, BlacklistHistoryRecord, UserMessage, TelegramPost, PostContent,
Admin, AudioMessage
)
class AsyncBotDB: class AsyncBotDB:

View File

@@ -1,6 +1,7 @@
import os import os
import aiosqlite
from typing import Optional from typing import Optional
import aiosqlite
from logs.custom_logger import logger from logs.custom_logger import logger

View File

@@ -1,6 +1,6 @@
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from typing import Optional, List from typing import List, Optional
@dataclass @dataclass

View File

@@ -11,13 +11,13 @@
- audio_repository: работа с аудио - audio_repository: работа с аудио
""" """
from .user_repository import UserRepository
from .blacklist_repository import BlacklistRepository
from .blacklist_history_repository import BlacklistHistoryRepository
from .message_repository import MessageRepository
from .post_repository import PostRepository
from .admin_repository import AdminRepository from .admin_repository import AdminRepository
from .audio_repository import AudioRepository from .audio_repository import AudioRepository
from .blacklist_history_repository import BlacklistHistoryRepository
from .blacklist_repository import BlacklistRepository
from .message_repository import MessageRepository
from .post_repository import PostRepository
from .user_repository import UserRepository
__all__ = [ __all__ = [
'UserRepository', 'BlacklistRepository', 'BlacklistHistoryRepository', 'UserRepository', 'BlacklistRepository', 'BlacklistHistoryRepository',

View File

@@ -1,4 +1,5 @@
from typing import Optional from typing import Optional
from database.base import DatabaseConnection from database.base import DatabaseConnection
from database.models import Admin from database.models import Admin

View File

@@ -1,7 +1,8 @@
from typing import Optional, List, Dict, Any
from database.base import DatabaseConnection
from database.models import AudioMessage, AudioListenRecord, AudioModerate
from datetime import datetime from datetime import datetime
from typing import Any, Dict, List, Optional
from database.base import DatabaseConnection
from database.models import AudioListenRecord, AudioMessage, AudioModerate
class AudioRepository(DatabaseConnection): class AudioRepository(DatabaseConnection):

View File

@@ -1,4 +1,5 @@
from typing import Optional from typing import Optional
from database.base import DatabaseConnection from database.base import DatabaseConnection
from database.models import BlacklistHistoryRecord from database.models import BlacklistHistoryRecord

View File

@@ -1,4 +1,5 @@
from typing import Optional, List, Dict from typing import Dict, List, Optional
from database.base import DatabaseConnection from database.base import DatabaseConnection
from database.models import BlacklistUser from database.models import BlacklistUser

View File

@@ -1,5 +1,6 @@
from datetime import datetime from datetime import datetime
from typing import Optional from typing import Optional
from database.base import DatabaseConnection from database.base import DatabaseConnection
from database.models import UserMessage from database.models import UserMessage

View File

@@ -1,7 +1,8 @@
from datetime import datetime from datetime import datetime
from typing import Optional, List, Tuple from typing import List, Optional, Tuple
from database.base import DatabaseConnection from database.base import DatabaseConnection
from database.models import TelegramPost, PostContent, MessageContentLink from database.models import MessageContentLink, PostContent, TelegramPost
class PostRepository(DatabaseConnection): class PostRepository(DatabaseConnection):

View File

@@ -1,5 +1,6 @@
from datetime import datetime from datetime import datetime
from typing import Optional, List, Dict, Any from typing import Any, Dict, List, Optional
from database.base import DatabaseConnection from database.base import DatabaseConnection
from database.models import User from database.models import User

View File

@@ -1,11 +1,13 @@
from typing import Optional from typing import Optional
from database.repositories.user_repository import UserRepository
from database.repositories.blacklist_repository import BlacklistRepository
from database.repositories.blacklist_history_repository import BlacklistHistoryRepository
from database.repositories.message_repository import MessageRepository
from database.repositories.post_repository import PostRepository
from database.repositories.admin_repository import AdminRepository from database.repositories.admin_repository import AdminRepository
from database.repositories.audio_repository import AudioRepository from database.repositories.audio_repository import AudioRepository
from database.repositories.blacklist_history_repository import \
BlacklistHistoryRepository
from database.repositories.blacklist_repository import BlacklistRepository
from database.repositories.message_repository import MessageRepository
from database.repositories.post_repository import PostRepository
from database.repositories.user_repository import UserRepository
class RepositoryFactory: class RepositoryFactory:

View File

@@ -1,20 +1,10 @@
from .admin_handlers import admin_router from .admin_handlers import admin_router
from .dependencies import AdminAccessMiddleware, BotDB, Settings from .dependencies import AdminAccessMiddleware, BotDB, Settings
from .services import AdminService, User, BannedUser from .exceptions import (AdminAccessDeniedError, AdminError, InvalidInputError,
from .exceptions import ( UserAlreadyBannedError, UserNotFoundError)
AdminError, from .services import AdminService, BannedUser, User
AdminAccessDeniedError, from .utils import (escape_html, format_ban_confirmation, format_user_info,
UserNotFoundError, handle_admin_error, return_to_admin_menu)
InvalidInputError,
UserAlreadyBannedError
)
from .utils import (
return_to_admin_menu,
handle_admin_error,
format_user_info,
format_ban_confirmation,
escape_html
)
__all__ = [ __all__ = [
'admin_router', 'admin_router',

View File

@@ -1,36 +1,24 @@
from aiogram import Router, types, F from aiogram import F, Router, types
from aiogram.filters import Command, StateFilter, MagicData from aiogram.filters import Command, MagicData, 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,
create_keyboard_for_ban_days,
create_keyboard_for_approve_ban,
create_keyboard_for_ban_reason
)
from helper_bot.handlers.admin.dependencies import AdminAccessMiddleware from helper_bot.handlers.admin.dependencies import AdminAccessMiddleware
from helper_bot.handlers.admin.exceptions import (InvalidInputError,
UserAlreadyBannedError)
from helper_bot.handlers.admin.services import AdminService from helper_bot.handlers.admin.services import AdminService
from helper_bot.handlers.admin.exceptions import ( from helper_bot.handlers.admin.utils import (escape_html,
UserAlreadyBannedError, format_ban_confirmation,
InvalidInputError format_user_info,
) handle_admin_error,
from helper_bot.handlers.admin.utils import ( return_to_admin_menu)
return_to_admin_menu, from helper_bot.keyboards.keyboards import (create_keyboard_for_approve_ban,
handle_admin_error, create_keyboard_for_ban_days,
format_user_info, create_keyboard_for_ban_reason,
format_ban_confirmation, create_keyboard_with_pagination,
escape_html get_reply_keyboard_admin)
)
from logs.custom_logger import logger
# Local imports - metrics # Local imports - metrics
from helper_bot.utils.metrics import ( from helper_bot.utils.metrics import db_query_time, track_errors, track_time
track_time, from logs.custom_logger import logger
track_errors,
db_query_time
)
# Создаем роутер с middleware для проверки доступа # Создаем роутер с middleware для проверки доступа
admin_router = Router() admin_router = Router()

View File

@@ -1,6 +1,6 @@
"""Constants for admin handlers""" """Constants for admin handlers"""
from typing import Final, Dict from typing import Dict, Final
# Admin button texts # Admin button texts
ADMIN_BUTTON_TEXTS: Final[Dict[str, str]] = { ADMIN_BUTTON_TEXTS: Final[Dict[str, str]] = {

View File

@@ -1,11 +1,12 @@
from typing import Dict, Any from typing import Any, Dict
try: try:
from typing import Annotated from typing import Annotated
except ImportError: except ImportError:
from typing_extensions import Annotated from typing_extensions import Annotated
from aiogram import BaseMiddleware from aiogram import BaseMiddleware
from aiogram.types import TelegramObject from aiogram.types import TelegramObject
from helper_bot.utils.base_dependency_factory import get_global_instance from helper_bot.utils.base_dependency_factory import get_global_instance
from helper_bot.utils.helper_func import check_access from helper_bot.utils.helper_func import check_access
from logs.custom_logger import logger from logs.custom_logger import logger

View File

@@ -1,22 +1,20 @@
""" """
Обработчики команд для мониторинга rate limiting Обработчики команд для мониторинга rate limiting
""" """
from aiogram import Router, types, F from aiogram import F, Router, types
from aiogram.filters import Command, MagicData from aiogram.filters import Command, MagicData
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.middlewares.dependencies_middleware import DependenciesMiddleware from helper_bot.middlewares.dependencies_middleware import \
from helper_bot.utils.rate_limit_monitor import rate_limit_monitor, get_rate_limit_summary DependenciesMiddleware
from helper_bot.utils.rate_limit_metrics import update_rate_limit_gauges, get_rate_limit_metrics_summary
from logs.custom_logger import logger
# Local imports - metrics # Local imports - metrics
from helper_bot.utils.metrics import ( from helper_bot.utils.metrics import track_errors, track_time
track_time, from helper_bot.utils.rate_limit_metrics import (
track_errors get_rate_limit_metrics_summary, update_rate_limit_gauges)
) from helper_bot.utils.rate_limit_monitor import (get_rate_limit_summary,
rate_limit_monitor)
from logs.custom_logger import logger
class RateLimitHandlers: class RateLimitHandlers:

View File

@@ -1,15 +1,14 @@
from typing import List, Optional
from datetime import datetime from datetime import datetime
from typing import List, Optional
from helper_bot.utils.helper_func import add_days_to_date, get_banned_users_buttons, get_banned_users_list from helper_bot.handlers.admin.exceptions import (InvalidInputError,
from helper_bot.handlers.admin.exceptions import UserAlreadyBannedError, InvalidInputError UserAlreadyBannedError)
from logs.custom_logger import logger from helper_bot.utils.helper_func import (add_days_to_date,
get_banned_users_buttons,
get_banned_users_list)
# Local imports - metrics # Local imports - metrics
from helper_bot.utils.metrics import ( from helper_bot.utils.metrics import track_errors, track_time
track_time, from logs.custom_logger import logger
track_errors
)
class User: class User:

View File

@@ -1,10 +1,10 @@
import html import html
from typing import Optional from typing import Optional
from aiogram import types from aiogram import types
from aiogram.fsm.context import FSMContext from aiogram.fsm.context import FSMContext
from helper_bot.keyboards.keyboards import get_reply_keyboard_admin
from helper_bot.handlers.admin.exceptions import AdminError from helper_bot.handlers.admin.exceptions import AdminError
from helper_bot.keyboards.keyboards import get_reply_keyboard_admin
from logs.custom_logger import logger from logs.custom_logger import logger

View File

@@ -1,10 +1,9 @@
from .callback_handlers import callback_router from .callback_handlers import callback_router
from .services import PostPublishService, BanService from .constants import (CALLBACK_BAN, CALLBACK_DECLINE, CALLBACK_PAGE,
from .exceptions import UserBlockedBotError, PostNotFoundError, UserNotFoundError, PublishError, BanError CALLBACK_PUBLISH, CALLBACK_RETURN, CALLBACK_UNLOCK)
from .constants import ( from .exceptions import (BanError, PostNotFoundError, PublishError,
CALLBACK_PUBLISH, CALLBACK_DECLINE, CALLBACK_BAN, CALLBACK_UNLOCK, UserBlockedBotError, UserNotFoundError)
CALLBACK_RETURN, CALLBACK_PAGE from .services import BanService, PostPublishService
)
__all__ = [ __all__ = [
'callback_router', 'callback_router',

View File

@@ -1,37 +1,34 @@
import html import html
import traceback
import time import time
import traceback
from datetime import datetime from datetime import datetime
from aiogram import Router, F from aiogram import F, Router
from aiogram.types import CallbackQuery
from aiogram.fsm.context import FSMContext
from aiogram.filters import MagicData from aiogram.filters import MagicData
from aiogram.fsm.context import FSMContext
from helper_bot.handlers.voice.constants import CALLBACK_SAVE, CALLBACK_DELETE from aiogram.types import CallbackQuery
from helper_bot.handlers.voice.services import AudioFileService
from helper_bot.keyboards.keyboards import create_keyboard_with_pagination, get_reply_keyboard_admin, \
create_keyboard_for_ban_reason
from helper_bot.utils.helper_func import get_banned_users_list, get_banned_users_buttons
from helper_bot.handlers.admin.utils import format_user_info from helper_bot.handlers.admin.utils import format_user_info
from helper_bot.handlers.voice.constants import CALLBACK_DELETE, CALLBACK_SAVE
from helper_bot.handlers.voice.services import AudioFileService
from helper_bot.keyboards.keyboards import (create_keyboard_for_ban_reason,
create_keyboard_with_pagination,
get_reply_keyboard_admin)
from helper_bot.utils.base_dependency_factory import get_global_instance from helper_bot.utils.base_dependency_factory import get_global_instance
from .dependency_factory import get_post_publish_service, get_ban_service from helper_bot.utils.helper_func import (get_banned_users_buttons,
from .exceptions import UserBlockedBotError, PostNotFoundError, UserNotFoundError, PublishError, BanError get_banned_users_list)
from .constants import ( # Local imports - metrics
CALLBACK_PUBLISH, CALLBACK_DECLINE, CALLBACK_BAN, CALLBACK_UNLOCK, from helper_bot.utils.metrics import (db_query_time, track_errors,
CALLBACK_RETURN, CALLBACK_PAGE, MESSAGE_PUBLISHED, MESSAGE_DECLINED, track_file_operations, track_time)
MESSAGE_USER_BANNED, MESSAGE_USER_UNLOCKED, MESSAGE_ERROR,
ERROR_BOT_BLOCKED
)
from logs.custom_logger import logger from logs.custom_logger import logger
# Local imports - metrics from .constants import (CALLBACK_BAN, CALLBACK_DECLINE, CALLBACK_PAGE,
from helper_bot.utils.metrics import ( CALLBACK_PUBLISH, CALLBACK_RETURN, CALLBACK_UNLOCK,
track_time, ERROR_BOT_BLOCKED, MESSAGE_DECLINED, MESSAGE_ERROR,
track_errors, MESSAGE_PUBLISHED, MESSAGE_USER_BANNED,
db_query_time, MESSAGE_USER_UNLOCKED)
track_file_operations from .dependency_factory import get_ban_service, get_post_publish_service
) from .exceptions import (BanError, PostNotFoundError, PublishError,
UserBlockedBotError, UserNotFoundError)
callback_router = Router() callback_router = Router()

View File

@@ -1,4 +1,4 @@
from typing import Final, Dict from typing import Dict, Final
# Callback data constants # Callback data constants
CALLBACK_PUBLISH = "publish" CALLBACK_PUBLISH = "publish"

View File

@@ -1,10 +1,11 @@
from typing import Callable from typing import Callable
from aiogram import Bot from aiogram import Bot
from aiogram.client.default import DefaultBotProperties from aiogram.client.default import DefaultBotProperties
from aiogram.fsm.context import FSMContext from aiogram.fsm.context import FSMContext
from helper_bot.utils.base_dependency_factory import get_global_instance from helper_bot.utils.base_dependency_factory import get_global_instance
from .services import PostPublishService, BanService
from .services import BanService, PostPublishService
def get_post_publish_service() -> PostPublishService: def get_post_publish_service() -> PostPublishService:

View File

@@ -1,36 +1,31 @@
from datetime import datetime, timedelta
import html import html
from typing import Dict, Any from datetime import datetime, timedelta
from typing import Any, Dict
from aiogram import Bot from aiogram import Bot, types
from aiogram import types
from aiogram.types import CallbackQuery from aiogram.types import CallbackQuery
from helper_bot.utils.helper_func import (
send_text_message, send_photo_message, send_video_message,
send_video_note_message, send_audio_message, send_voice_message,
send_media_group_to_channel, delete_user_blacklist, get_text_message
)
from helper_bot.keyboards.keyboards import create_keyboard_for_ban_reason from helper_bot.keyboards.keyboards import create_keyboard_for_ban_reason
from .exceptions import ( from helper_bot.utils.helper_func import (delete_user_blacklist,
UserBlockedBotError, PostNotFoundError, UserNotFoundError, get_text_message, send_audio_message,
PublishError, BanError send_media_group_to_channel,
) send_photo_message,
from .constants import ( send_text_message,
CONTENT_TYPE_TEXT, CONTENT_TYPE_PHOTO, CONTENT_TYPE_VIDEO, send_video_message,
CONTENT_TYPE_VIDEO_NOTE, CONTENT_TYPE_AUDIO, CONTENT_TYPE_VOICE, send_video_note_message,
CONTENT_TYPE_MEDIA_GROUP, MESSAGE_POST_PUBLISHED, MESSAGE_POST_DECLINED, send_voice_message)
MESSAGE_USER_BANNED_SPAM, ERROR_BOT_BLOCKED # Local imports - metrics
) from helper_bot.utils.metrics import (db_query_time, track_errors,
track_media_processing, track_time)
from logs.custom_logger import logger from logs.custom_logger import logger
# Local imports - metrics from .constants import (CONTENT_TYPE_AUDIO, CONTENT_TYPE_MEDIA_GROUP,
from helper_bot.utils.metrics import ( CONTENT_TYPE_PHOTO, CONTENT_TYPE_TEXT,
track_media_processing, CONTENT_TYPE_VIDEO, CONTENT_TYPE_VIDEO_NOTE,
track_time, CONTENT_TYPE_VOICE, ERROR_BOT_BLOCKED,
track_errors, MESSAGE_POST_DECLINED, MESSAGE_POST_PUBLISHED,
db_query_time MESSAGE_USER_BANNED_SPAM)
) from .exceptions import (BanError, PostNotFoundError, PublishError,
UserBlockedBotError, UserNotFoundError)
class PostPublishService: class PostPublishService:

View File

@@ -1,28 +1,13 @@
"""Group handlers package for Telegram bot""" """Group handlers package for Telegram bot"""
# Local imports - main components # Local imports - main components
from .group_handlers import (
group_router,
create_group_handlers,
GroupHandlers
)
# Local imports - services
from .services import (
AdminReplyService,
DatabaseProtocol
)
# Local imports - constants and utilities # Local imports - constants and utilities
from .constants import ( from .constants import ERROR_MESSAGES, FSM_STATES
FSM_STATES,
ERROR_MESSAGES
)
from .exceptions import (
NoReplyToMessageError,
UserNotFoundError
)
from .decorators import error_handler from .decorators import error_handler
from .exceptions import NoReplyToMessageError, UserNotFoundError
from .group_handlers import GroupHandlers, create_group_handlers, group_router
# Local imports - services
from .services import AdminReplyService, DatabaseProtocol
__all__ = [ __all__ = [
# Main components # Main components

View File

@@ -1,6 +1,6 @@
"""Constants for group handlers""" """Constants for group handlers"""
from typing import Final, Dict from typing import Dict, Final
# FSM States # FSM States
FSM_STATES: Final[Dict[str, str]] = { FSM_STATES: Final[Dict[str, str]] = {

View File

@@ -6,7 +6,6 @@ from typing import Any, Callable
# Third-party imports # Third-party imports
from aiogram import types from aiogram import types
# Local imports # Local imports
from logs.custom_logger import logger from logs.custom_logger import logger
@@ -22,7 +21,8 @@ def error_handler(func: Callable[..., Any]) -> Callable[..., Any]:
try: try:
message = next((arg for arg in args if isinstance(arg, types.Message)), None) message = next((arg for arg in args if isinstance(arg, types.Message)), None)
if message and hasattr(message, 'bot'): if message and hasattr(message, 'bot'):
from helper_bot.utils.base_dependency_factory import get_global_instance from helper_bot.utils.base_dependency_factory import \
get_global_instance
bdf = get_global_instance() bdf = get_global_instance()
important_logs = bdf.settings['Telegram']['important_logs'] important_logs = bdf.settings['Telegram']['important_logs']
await message.bot.send_message( await message.bot.send_message(

View File

@@ -3,26 +3,20 @@
# Third-party imports # Third-party imports
from aiogram import Router, types from aiogram import Router, types
from aiogram.fsm.context import FSMContext from aiogram.fsm.context import FSMContext
# Local imports - filters # Local imports - filters
from database.async_db import AsyncBotDB from database.async_db import AsyncBotDB
from helper_bot.filters.main import ChatTypeFilter from helper_bot.filters.main import ChatTypeFilter
# Local imports - metrics
# Local imports - modular components from helper_bot.utils.metrics import metrics, track_errors, track_time
from .constants import FSM_STATES, ERROR_MESSAGES
from .services import AdminReplyService
from .decorators import error_handler
from .exceptions import UserNotFoundError
# Local imports - utilities # Local imports - utilities
from logs.custom_logger import logger from logs.custom_logger import logger
# Local imports - metrics # Local imports - modular components
from helper_bot.utils.metrics import ( from .constants import ERROR_MESSAGES, FSM_STATES
metrics, from .decorators import error_handler
track_time, from .exceptions import UserNotFoundError
track_errors from .services import AdminReplyService
)
class GroupHandlers: class GroupHandlers:
"""Main handler class for group messages""" """Main handler class for group messages"""
@@ -102,8 +96,8 @@ def init_legacy_router():
"""Initialize legacy router with global dependencies""" """Initialize legacy router with global dependencies"""
global group_router global group_router
from helper_bot.utils.base_dependency_factory import get_global_instance
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
bdf = get_global_instance() bdf = get_global_instance()
#TODO: поменять архитектуру и подключить правильный BotDB #TODO: поменять архитектуру и подключить правильный BotDB

View File

@@ -1,22 +1,17 @@
"""Service classes for group handlers""" """Service classes for group handlers"""
# Standard library imports # Standard library imports
from typing import Protocol, Optional from typing import Optional, Protocol
# Third-party imports # Third-party imports
from aiogram import types from aiogram import types
# Local imports # Local imports
from helper_bot.utils.helper_func import send_text_message from helper_bot.utils.helper_func import send_text_message
from .exceptions import NoReplyToMessageError, UserNotFoundError # Local imports - metrics
from helper_bot.utils.metrics import db_query_time, track_errors, track_time
from logs.custom_logger import logger from logs.custom_logger import logger
# Local imports - metrics from .exceptions import NoReplyToMessageError, UserNotFoundError
from helper_bot.utils.metrics import (
track_time,
track_errors,
db_query_time
)
class DatabaseProtocol(Protocol): class DatabaseProtocol(Protocol):

View File

@@ -1,27 +1,13 @@
"""Private handlers package for Telegram bot""" """Private handlers package for Telegram bot"""
# Local imports - main components # Local imports - main components
from .private_handlers import (
private_router,
create_private_handlers,
PrivateHandlers
)
# Local imports - services
from .services import (
BotSettings,
UserService,
PostService,
StickerService
)
# Local imports - constants and utilities # Local imports - constants and utilities
from .constants import ( from .constants import BUTTON_TEXTS, ERROR_MESSAGES, FSM_STATES
FSM_STATES,
BUTTON_TEXTS,
ERROR_MESSAGES
)
from .decorators import error_handler from .decorators import error_handler
from .private_handlers import (PrivateHandlers, create_private_handlers,
private_router)
# Local imports - services
from .services import BotSettings, PostService, StickerService, UserService
__all__ = [ __all__ = [
# Main components # Main components

View File

@@ -1,6 +1,6 @@
"""Constants for private handlers""" """Constants for private handlers"""
from typing import Final, Dict from typing import Dict, Final
# FSM States # FSM States
FSM_STATES: Final[Dict[str, str]] = { FSM_STATES: Final[Dict[str, str]] = {

View File

@@ -6,7 +6,6 @@ from typing import Any, Callable
# Third-party imports # Third-party imports
from aiogram import types from aiogram import types
# Local imports # Local imports
from logs.custom_logger import logger from logs.custom_logger import logger
@@ -22,7 +21,8 @@ def error_handler(func: Callable[..., Any]) -> Callable[..., Any]:
try: try:
message = next((arg for arg in args if isinstance(arg, types.Message)), None) message = next((arg for arg in args if isinstance(arg, types.Message)), None)
if message and hasattr(message, 'bot'): if message and hasattr(message, 'bot'):
from helper_bot.utils.base_dependency_factory import get_global_instance from helper_bot.utils.base_dependency_factory import \
get_global_instance
bdf = get_global_instance() bdf = get_global_instance()
important_logs = bdf.settings['Telegram']['important_logs'] important_logs = bdf.settings['Telegram']['important_logs']
await message.bot.send_message( await message.bot.send_message(

View File

@@ -5,37 +5,28 @@ import asyncio
from datetime import datetime from datetime import datetime
# Third-party imports # Third-party imports
from aiogram import types, Router, F from aiogram import F, Router, types
from aiogram.filters import Command, StateFilter from aiogram.filters import Command, StateFilter
from aiogram.fsm.context import FSMContext from aiogram.fsm.context import FSMContext
# Local imports - filters and middlewares # Local imports - filters and middlewares
from database.async_db import AsyncBotDB from database.async_db import AsyncBotDB
from helper_bot.filters.main import ChatTypeFilter from helper_bot.filters.main import ChatTypeFilter
# Local imports - utilities
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.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
# Local imports - utilities
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.utils import messages from helper_bot.utils import messages
from helper_bot.utils.helper_func import ( from helper_bot.utils.helper_func import (check_user_emoji, get_first_name,
get_first_name, update_user_info)
update_user_info,
check_user_emoji
)
# Local imports - metrics # Local imports - metrics
from helper_bot.utils.metrics import ( from helper_bot.utils.metrics import db_query_time, track_errors, track_time
track_time,
track_errors,
db_query_time
)
# Local imports - modular components # Local imports - modular components
from .constants import FSM_STATES, BUTTON_TEXTS, ERROR_MESSAGES from .constants import BUTTON_TEXTS, ERROR_MESSAGES, FSM_STATES
from .services import BotSettings, UserService, PostService, StickerService
from .decorators import error_handler from .decorators import error_handler
from .services import BotSettings, PostService, StickerService, UserService
# Expose sleep for tests (tests patch helper_bot.handlers.private.private_handlers.sleep) # Expose sleep for tests (tests patch helper_bot.handlers.private.private_handlers.sleep)
sleep = asyncio.sleep sleep = asyncio.sleep

View File

@@ -1,46 +1,31 @@
"""Service classes for private handlers""" """Service classes for private handlers"""
# Standard library imports # Standard library imports
import random
import asyncio import asyncio
import html import html
import random
from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Dict, Callable, Any, Protocol, Union from typing import Any, Callable, Dict, Protocol, Union
from dataclasses import dataclass
# Third-party imports # Third-party imports
from aiogram import types from aiogram import types
from aiogram.types import FSInputFile from aiogram.types import FSInputFile
from database.models import TelegramPost, User from database.models import TelegramPost, User
from logs.custom_logger import logger from helper_bot.keyboards import get_reply_keyboard_for_post
# Local imports - utilities # Local imports - utilities
from helper_bot.utils.helper_func import ( from helper_bot.utils.helper_func import (
get_first_name, add_in_db_media, check_username_and_full_name, determine_anonymity,
get_text_message, get_first_name, get_text_message, prepare_media_group_from_middlewares,
determine_anonymity, send_audio_message, send_media_group_message_to_private_chat,
send_text_message, send_photo_message, send_text_message, send_video_message,
send_photo_message, send_video_note_message, send_voice_message)
send_media_group_message_to_private_chat,
prepare_media_group_from_middlewares,
send_video_message,
send_video_note_message,
send_audio_message,
send_voice_message,
add_in_db_media,
check_username_and_full_name
)
from helper_bot.keyboards import get_reply_keyboard_for_post
# Local imports - metrics # Local imports - metrics
from helper_bot.utils.metrics import ( from helper_bot.utils.metrics import (db_query_time, track_errors,
track_time, track_file_operations,
track_errors, track_media_processing, track_time)
db_query_time, from logs.custom_logger import logger
track_media_processing,
track_file_operations
)
class DatabaseProtocol(Protocol): class DatabaseProtocol(Protocol):

View File

@@ -1,12 +1,13 @@
""" """
Утилиты для очистки и диагностики проблем с голосовыми файлами Утилиты для очистки и диагностики проблем с голосовыми файлами
""" """
import os
import asyncio import asyncio
import os
from pathlib import Path from pathlib import Path
from typing import List, Tuple from typing import List, Tuple
from logs.custom_logger import logger
from helper_bot.handlers.voice.constants import VOICE_USERS_DIR from helper_bot.handlers.voice.constants import VOICE_USERS_DIR
from logs.custom_logger import logger
class VoiceFileCleanupUtils: class VoiceFileCleanupUtils:

View File

@@ -1,4 +1,4 @@
from typing import Final, Dict from typing import Dict, Final
# Voice bot constants # Voice bot constants
VOICE_BOT_NAME = "voice" VOICE_BOT_NAME = "voice"

View File

@@ -1,26 +1,26 @@
import random
import asyncio import asyncio
import traceback
import os import os
import random
import traceback
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import List, Optional, Tuple from typing import List, Optional, Tuple
from aiogram.types import FSInputFile from aiogram.types import FSInputFile
from helper_bot.handlers.voice.constants import (MESSAGE_DELAY_1,
from helper_bot.handlers.voice.exceptions import VoiceMessageError, AudioProcessingError, DatabaseError, FileOperationError MESSAGE_DELAY_2,
from helper_bot.handlers.voice.constants import ( MESSAGE_DELAY_3,
VOICE_USERS_DIR, STICK_DIR, STICK_PATTERN, STICKER_DELAY, MESSAGE_DELAY_4, STICK_DIR,
MESSAGE_DELAY_1, MESSAGE_DELAY_2, MESSAGE_DELAY_3, MESSAGE_DELAY_4 STICK_PATTERN, STICKER_DELAY,
) VOICE_USERS_DIR)
from helper_bot.handlers.voice.exceptions import (AudioProcessingError,
DatabaseError,
FileOperationError,
VoiceMessageError)
# Local imports - metrics
from helper_bot.utils.metrics import db_query_time, track_errors, track_time
from logs.custom_logger import logger from logs.custom_logger import logger
# Local imports - metrics
from helper_bot.utils.metrics import (
track_time,
track_errors,
db_query_time
)
class VoiceMessage: class VoiceMessage:
"""Модель голосового сообщения""" """Модель голосового сообщения"""

View File

@@ -1,16 +1,12 @@
import time
import html import html
import time
from datetime import datetime from datetime import datetime
from typing import Optional from typing import Optional
from helper_bot.handlers.voice.exceptions import DatabaseError from helper_bot.handlers.voice.exceptions import DatabaseError
from helper_bot.utils.metrics import db_query_time, track_errors, track_time
from logs.custom_logger import logger from logs.custom_logger import logger
from helper_bot.utils.metrics import (
track_time,
track_errors,
db_query_time
)
def format_time_ago(date_from_db: str) -> Optional[str]: def format_time_ago(date_from_db: str) -> Optional[str]:
"""Форматировать время с момента последней записи""" """Форматировать время с момента последней записи"""

View File

@@ -2,33 +2,31 @@ import asyncio
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from aiogram import Router, types, F from aiogram import F, Router, types
from aiogram.filters import Command, StateFilter, MagicData from aiogram.filters import Command, MagicData, 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.middlewares.blacklist_middleware import BlacklistMiddleware from helper_bot.handlers.private.constants import BUTTON_TEXTS, FSM_STATES
from helper_bot.middlewares.dependencies_middleware import DependenciesMiddleware
from helper_bot.utils import messages
from helper_bot.utils.helper_func import get_first_name, update_user_info, check_user_emoji, send_voice_message
from logs.custom_logger import logger
from helper_bot.handlers.voice.constants import * from helper_bot.handlers.voice.constants import *
from helper_bot.handlers.voice.services import VoiceBotService from helper_bot.handlers.voice.services import VoiceBotService
from helper_bot.handlers.voice.utils import get_last_message_text, validate_voice_message, get_user_emoji_safe from helper_bot.handlers.voice.utils import (get_last_message_text,
from helper_bot.keyboards.keyboards import get_main_keyboard, get_reply_keyboard_for_voice get_user_emoji_safe,
validate_voice_message)
from helper_bot.keyboards import get_reply_keyboard from helper_bot.keyboards import get_reply_keyboard
from helper_bot.handlers.private.constants import FSM_STATES from helper_bot.keyboards.keyboards import (get_main_keyboard,
from helper_bot.handlers.private.constants import BUTTON_TEXTS get_reply_keyboard_for_voice)
from helper_bot.middlewares.blacklist_middleware import BlacklistMiddleware
from helper_bot.middlewares.dependencies_middleware import \
DependenciesMiddleware
from helper_bot.utils import messages
from helper_bot.utils.helper_func import (check_user_emoji, get_first_name,
send_voice_message, update_user_info)
# Local imports - metrics # Local imports - metrics
from helper_bot.utils.metrics import ( from helper_bot.utils.metrics import (db_query_time, track_errors,
track_time, track_file_operations, track_time)
track_errors, from logs.custom_logger import logger
db_query_time,
track_file_operations
)
class VoiceHandlers: class VoiceHandlers:
def __init__(self, db, settings): def __init__(self, db, settings):

View File

@@ -1 +1 @@
from .keyboards import get_reply_keyboard_for_post, get_reply_keyboard from .keyboards import get_reply_keyboard, get_reply_keyboard_for_post

View File

@@ -1,11 +1,7 @@
from aiogram import types from aiogram import types
from aiogram.utils.keyboard import ReplyKeyboardBuilder, InlineKeyboardBuilder from aiogram.utils.keyboard import InlineKeyboardBuilder, ReplyKeyboardBuilder
# Local imports - metrics # Local imports - metrics
from helper_bot.utils.metrics import ( from helper_bot.utils.metrics import track_errors, track_time
track_time,
track_errors
)
def get_reply_keyboard_for_post(): def get_reply_keyboard_for_post():

View File

@@ -1,21 +1,24 @@
import asyncio
import logging
from typing import Optional
from aiogram import Bot, Dispatcher from aiogram import Bot, Dispatcher
from aiogram.client.default import DefaultBotProperties 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
import logging
import asyncio
from typing import Optional
from helper_bot.handlers.admin import admin_router from helper_bot.handlers.admin import admin_router
from helper_bot.handlers.callback import callback_router from helper_bot.handlers.callback import callback_router
from helper_bot.handlers.group import group_router from helper_bot.handlers.group import group_router
from helper_bot.handlers.private import private_router from helper_bot.handlers.private import private_router
from helper_bot.handlers.voice import VoiceHandlers from helper_bot.handlers.voice import VoiceHandlers
from helper_bot.middlewares.dependencies_middleware import DependenciesMiddleware
from helper_bot.middlewares.blacklist_middleware import BlacklistMiddleware from helper_bot.middlewares.blacklist_middleware import BlacklistMiddleware
from helper_bot.middlewares.metrics_middleware import MetricsMiddleware, ErrorMetricsMiddleware from helper_bot.middlewares.dependencies_middleware import \
DependenciesMiddleware
from helper_bot.middlewares.metrics_middleware import (ErrorMetricsMiddleware,
MetricsMiddleware)
from helper_bot.middlewares.rate_limit_middleware import RateLimitMiddleware from helper_bot.middlewares.rate_limit_middleware import RateLimitMiddleware
from helper_bot.server_prometheus import start_metrics_server, stop_metrics_server from helper_bot.server_prometheus import (start_metrics_server,
stop_metrics_server)
async def start_bot_with_retry(bot: Bot, dp: Dispatcher, max_retries: int = 5, base_delay: float = 1.0): async def start_bot_with_retry(bot: Bot, dp: Dispatcher, max_retries: int = 5, base_delay: float = 1.0):

View File

@@ -1,5 +1,5 @@
import asyncio import asyncio
from typing import Any, Dict, Union, List, Optional from typing import Any, Dict, List, Optional, Union
from aiogram import BaseMiddleware from aiogram import BaseMiddleware
from aiogram.types import Message from aiogram.types import Message

View File

@@ -1,9 +1,9 @@
from typing import Dict, Any
import html import html
from datetime import datetime from datetime import datetime
from typing import Any, Dict
from aiogram import BaseMiddleware, types from aiogram import BaseMiddleware, types
from aiogram.types import TelegramObject, Message, CallbackQuery from aiogram.types import CallbackQuery, Message, TelegramObject
from helper_bot.utils.base_dependency_factory import get_global_instance from helper_bot.utils.base_dependency_factory import get_global_instance
from logs.custom_logger import logger from logs.custom_logger import logger

View File

@@ -1,7 +1,7 @@
from typing import Any, Dict from typing import Any, Dict
from aiogram import BaseMiddleware from aiogram import BaseMiddleware
from aiogram.types import TelegramObject from aiogram.types import TelegramObject
from helper_bot.utils.base_dependency_factory import get_global_instance from helper_bot.utils.base_dependency_factory import get_global_instance
from logs.custom_logger import logger from logs.custom_logger import logger

View File

@@ -3,25 +3,29 @@ Enhanced Metrics middleware for aiogram 3.x.
Automatically collects ALL available metrics for comprehensive monitoring. Automatically collects ALL available metrics for comprehensive monitoring.
""" """
from typing import Any, Awaitable, Callable, Dict, Union, Optional
from aiogram import BaseMiddleware
from aiogram.types import TelegramObject, Message, CallbackQuery
from aiogram.enums import ChatType
import time
import logging
import asyncio import asyncio
import logging
import time
from typing import Any, Awaitable, Callable, Dict, Optional, Union
from aiogram import BaseMiddleware
from aiogram.enums import ChatType
from aiogram.types import CallbackQuery, Message, TelegramObject
from ..utils.metrics import metrics from ..utils.metrics import metrics
# Import button command mapping # Import button command mapping
try: try:
from ..handlers.private.constants import BUTTON_COMMAND_MAPPING from ..handlers.admin.constants import (ADMIN_BUTTON_COMMAND_MAPPING,
ADMIN_COMMANDS)
from ..handlers.callback.constants import CALLBACK_COMMAND_MAPPING from ..handlers.callback.constants import CALLBACK_COMMAND_MAPPING
from ..handlers.admin.constants import ADMIN_BUTTON_COMMAND_MAPPING, ADMIN_COMMANDS from ..handlers.private.constants import BUTTON_COMMAND_MAPPING
from ..handlers.voice.constants import ( from ..handlers.voice.constants import \
BUTTON_COMMAND_MAPPING as VOICE_BUTTON_COMMAND_MAPPING, BUTTON_COMMAND_MAPPING as VOICE_BUTTON_COMMAND_MAPPING
COMMAND_MAPPING as VOICE_COMMAND_MAPPING, from ..handlers.voice.constants import \
CALLBACK_COMMAND_MAPPING as VOICE_CALLBACK_COMMAND_MAPPING CALLBACK_COMMAND_MAPPING as VOICE_CALLBACK_COMMAND_MAPPING
) from ..handlers.voice.constants import \
COMMAND_MAPPING as VOICE_COMMAND_MAPPING
except ImportError: except ImportError:
# Fallback if constants not available # Fallback if constants not available
BUTTON_COMMAND_MAPPING = {} BUTTON_COMMAND_MAPPING = {}

View File

@@ -1,13 +1,14 @@
""" """
Middleware для автоматического применения rate limiting ко всем входящим сообщениям Middleware для автоматического применения rate limiting ко всем входящим сообщениям
""" """
from typing import Callable, Dict, Any, Awaitable, Union from typing import Any, Awaitable, Callable, Dict, Union
from aiogram import BaseMiddleware
from aiogram.types import Message, CallbackQuery, InlineQuery, ChatMemberUpdated, Update
from aiogram.exceptions import TelegramRetryAfter, TelegramAPIError
from logs.custom_logger import logger
from aiogram import BaseMiddleware
from aiogram.exceptions import TelegramAPIError, TelegramRetryAfter
from aiogram.types import (CallbackQuery, ChatMemberUpdated, InlineQuery,
Message, Update)
from helper_bot.utils.rate_limiter import telegram_rate_limiter from helper_bot.utils.rate_limiter import telegram_rate_limiter
from logs.custom_logger import logger
class RateLimitMiddleware(BaseMiddleware): class RateLimitMiddleware(BaseMiddleware):

View File

@@ -5,8 +5,10 @@ Provides /metrics endpoint and health check for the bot.
""" """
import asyncio import asyncio
from aiohttp import web
from typing import Optional from typing import Optional
from aiohttp import web
from .utils.metrics import metrics from .utils.metrics import metrics
# Импортируем логгер из проекта # Импортируем логгер из проекта

View File

@@ -1,18 +1,14 @@
import asyncio import asyncio
from datetime import datetime, timezone, timedelta from datetime import datetime, timedelta, timezone
from typing import Optional from typing import Optional
from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.cron import CronTrigger from apscheduler.triggers.cron import CronTrigger
from helper_bot.utils.base_dependency_factory import get_global_instance from helper_bot.utils.base_dependency_factory import get_global_instance
from logs.custom_logger import logger from logs.custom_logger import logger
from .metrics import ( from .metrics import db_query_time, track_errors, track_time
track_time,
track_errors,
db_query_time
)
class AutoUnbanScheduler: class AutoUnbanScheduler:
""" """

View File

@@ -1,9 +1,9 @@
import os import os
import sys import sys
from typing import Optional from typing import Optional
from dotenv import load_dotenv
from database.async_db import AsyncBotDB from database.async_db import AsyncBotDB
from dotenv import load_dotenv
from helper_bot.utils.s3_storage import S3StorageService from helper_bot.utils.s3_storage import S3StorageService

View File

@@ -1,12 +1,12 @@
import asyncio
import html import html
import os import os
import random import random
import time
import tempfile import tempfile
import asyncio import time
from datetime import datetime, timedelta from datetime import datetime, timedelta
from time import sleep from time import sleep
from typing import List, Dict, Any, Optional, TYPE_CHECKING, Union from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
try: try:
import emoji as _emoji_lib import emoji as _emoji_lib
@@ -16,20 +16,16 @@ except ImportError:
_emoji_lib_available = False _emoji_lib_available = False
from aiogram import types from aiogram import types
from aiogram.types import InputMediaPhoto, FSInputFile, InputMediaVideo, InputMediaAudio, InputMediaDocument from aiogram.types import (FSInputFile, InputMediaAudio, InputMediaDocument,
InputMediaPhoto, InputMediaVideo)
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory, get_global_instance
from logs.custom_logger import logger
from database.models import TelegramPost from database.models import TelegramPost
from helper_bot.utils.base_dependency_factory import (BaseDependencyFactory,
get_global_instance)
from logs.custom_logger import logger
# Local imports - metrics # Local imports - metrics
from .metrics import ( from .metrics import (db_query_time, track_errors, track_file_operations,
track_time, track_media_processing, track_time)
track_errors,
db_query_time,
track_media_processing,
track_file_operations,
)
bdf = get_global_instance() bdf = get_global_instance()
#TODO: поменять архитектуру и подключить правильный BotDB #TODO: поменять архитектуру и подключить правильный BotDB
@@ -653,7 +649,7 @@ async def send_media_group_to_channel(bot, chat_id: int, post_content: List, pos
@track_errors("helper_func", "send_text_message") @track_errors("helper_func", "send_text_message")
async def send_text_message(chat_id, message: types.Message, post_text: str, markup: types.ReplyKeyboardMarkup = None): async def send_text_message(chat_id, message: types.Message, post_text: str, markup: types.ReplyKeyboardMarkup = None):
from .rate_limiter import send_with_rate_limit from .rate_limiter import send_with_rate_limit
# Экранируем post_text для безопасного использования в HTML # Экранируем post_text для безопасного использования в HTML
safe_post_text = html.escape(str(post_text)) if post_text else "" safe_post_text = html.escape(str(post_text)) if post_text else ""

View File

@@ -1,12 +1,7 @@
import html import html
# Local imports - metrics # Local imports - metrics
from .metrics import ( from .metrics import metrics, track_errors, track_time
metrics,
track_time,
track_errors
)
constants = { constants = {
'HELLO_MESSAGE': "Привет, username!👋🏼&Меня зовут Виби, я бот канала 'Влюбленный Бийск'❤🤖" 'HELLO_MESSAGE': "Привет, username!👋🏼&Меня зовут Виби, я бот канала 'Влюбленный Бийск'❤🤖"

View File

@@ -3,14 +3,16 @@ Metrics module for Telegram bot monitoring with Prometheus.
Provides predefined metrics for bot commands, errors, performance, and user activity. Provides predefined metrics for bot commands, errors, performance, and user activity.
""" """
from typing import Dict, Any, Optional
from prometheus_client import Counter, Histogram, Gauge, generate_latest, CONTENT_TYPE_LATEST
from prometheus_client.core import CollectorRegistry
import time
import os
from functools import wraps
import asyncio import asyncio
import os
import time
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from functools import wraps
from typing import Any, Dict, Optional
from prometheus_client import (CONTENT_TYPE_LATEST, Counter, Gauge, Histogram,
generate_latest)
from prometheus_client.core import CollectorRegistry
# Метрики rate limiter теперь создаются в основном классе # Метрики rate limiter теперь создаются в основном классе
@@ -387,7 +389,7 @@ class BotMetrics:
"""Update rate limit gauge metrics.""" """Update rate limit gauge metrics."""
try: try:
from .rate_limit_monitor import rate_limit_monitor from .rate_limit_monitor import rate_limit_monitor
# Обновляем количество активных чатов # Обновляем количество активных чатов
self.rate_limit_active_chats.set(len(rate_limit_monitor.stats)) self.rate_limit_active_chats.set(len(rate_limit_monitor.stats))

View File

@@ -2,9 +2,10 @@
Мониторинг и статистика rate limiting Мониторинг и статистика rate limiting
""" """
import time import time
from typing import Dict, List, Optional
from dataclasses import dataclass, field
from collections import defaultdict, deque from collections import defaultdict, deque
from dataclasses import dataclass, field
from typing import Dict, List, Optional
from logs.custom_logger import logger from logs.custom_logger import logger

View File

@@ -3,10 +3,12 @@ Rate limiter для предотвращения Flood control ошибок в T
""" """
import asyncio import asyncio
import time import time
from typing import Dict, Optional, Any, Callable
from dataclasses import dataclass from dataclasses import dataclass
from aiogram.exceptions import TelegramRetryAfter, TelegramAPIError from typing import Any, Callable, Dict, Optional
from aiogram.exceptions import TelegramAPIError, TelegramRetryAfter
from logs.custom_logger import logger from logs.custom_logger import logger
from .metrics import metrics from .metrics import metrics
@@ -182,7 +184,9 @@ class TelegramRateLimiter:
# Глобальный экземпляр rate limiter # Глобальный экземпляр rate limiter
from helper_bot.config.rate_limit_config import get_rate_limit_config, RateLimitSettings from helper_bot.config.rate_limit_config import (RateLimitSettings,
get_rate_limit_config)
def _create_rate_limit_config(settings: RateLimitSettings) -> RateLimitConfig: def _create_rate_limit_config(settings: RateLimitSettings) -> RateLimitConfig:
"""Создает RateLimitConfig из RateLimitSettings""" """Создает RateLimitConfig из RateLimitSettings"""

View File

@@ -1,11 +1,12 @@
""" """
Сервис для работы с S3 хранилищем. Сервис для работы с S3 хранилищем.
""" """
import aioboto3
import os import os
import tempfile import tempfile
from typing import Optional
from pathlib import Path from pathlib import Path
from typing import Optional
import aioboto3
from logs.custom_logger import logger from logs.custom_logger import logger

View File

@@ -1,4 +1,4 @@
from aiogram.fsm.state import StatesGroup, State from aiogram.fsm.state import State, StatesGroup
class StateUser(StatesGroup): class StateUser(StatesGroup):

View File

@@ -1,6 +1,7 @@
import datetime import datetime
import os import os
import sys import sys
from loguru import logger from loguru import logger
# Remove default handler # Remove default handler

View File

@@ -1,8 +1,8 @@
import asyncio import asyncio
import os import os
import sys
import signal import signal
import sqlite3 import sqlite3
import sys
# Ensure project root is on sys.path for module resolution # Ensure project root is on sys.path for module resolution
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -10,12 +10,11 @@ if CURRENT_DIR not in sys.path:
sys.path.insert(0, CURRENT_DIR) sys.path.insert(0, CURRENT_DIR)
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.auto_unban_scheduler import get_auto_unban_scheduler from helper_bot.utils.auto_unban_scheduler import get_auto_unban_scheduler
from helper_bot.utils.base_dependency_factory import get_global_instance
from logs.custom_logger import logger from logs.custom_logger import logger
async def main(): async def main():
"""Основная функция запуска""" """Основная функция запуска"""
@@ -69,7 +68,8 @@ async def main():
# Останавливаем планировщик метрик # Останавливаем планировщик метрик
try: try:
from helper_bot.utils.metrics_scheduler import stop_metrics_scheduler from helper_bot.utils.metrics_scheduler import \
stop_metrics_scheduler
stop_metrics_scheduler() stop_metrics_scheduler()
logger.info("Планировщик метрик остановлен") logger.info("Планировщик метрик остановлен")
except Exception as e: except Exception as e:

View File

@@ -13,9 +13,8 @@ project_root = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(project_root)) sys.path.insert(0, str(project_root))
import aiosqlite import aiosqlite
from logs.custom_logger import logger
from helper_bot.utils.helper_func import determine_anonymity from helper_bot.utils.helper_func import determine_anonymity
from logs.custom_logger import logger
DEFAULT_DB_PATH = "database/tg-bot-database.db" DEFAULT_DB_PATH = "database/tg-bot-database.db"

View File

@@ -15,7 +15,6 @@ project_root = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(project_root)) sys.path.insert(0, str(project_root))
import aiosqlite import aiosqlite
from logs.custom_logger import logger from logs.custom_logger import logger
DEFAULT_DB_PATH = "database/tg-bot-database.db" DEFAULT_DB_PATH = "database/tg-bot-database.db"

View File

@@ -13,7 +13,6 @@ project_root = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(project_root)) sys.path.insert(0, str(project_root))
import aiosqlite import aiosqlite
from logs.custom_logger import logger from logs.custom_logger import logger
DEFAULT_DB_PATH = "database/tg-bot-database.db" DEFAULT_DB_PATH = "database/tg-bot-database.db"

View File

@@ -15,7 +15,6 @@ project_root = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(project_root)) sys.path.insert(0, str(project_root))
import aiosqlite import aiosqlite
from logs.custom_logger import logger from logs.custom_logger import logger
DEFAULT_DB_PATH = "database/tg-bot-database.db" DEFAULT_DB_PATH = "database/tg-bot-database.db"

View File

@@ -13,7 +13,6 @@ project_root = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(project_root)) sys.path.insert(0, str(project_root))
import aiosqlite import aiosqlite
from logs.custom_logger import logger from logs.custom_logger import logger
DEFAULT_DB_PATH = "database/tg-bot-database.db" DEFAULT_DB_PATH = "database/tg-bot-database.db"

View File

@@ -7,14 +7,13 @@ import argparse
import asyncio import asyncio
import os import os
import sys import sys
from pathlib import Path
from datetime import datetime from datetime import datetime
from pathlib import Path
project_root = Path(__file__).resolve().parent.parent project_root = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(project_root)) sys.path.insert(0, str(project_root))
import aiosqlite import aiosqlite
from logs.custom_logger import logger from logs.custom_logger import logger
DEFAULT_DB_PATH = "database/tg-bot-database.db" DEFAULT_DB_PATH = "database/tg-bot-database.db"

View File

@@ -13,6 +13,7 @@ sys.path.insert(0, str(project_root))
# Загружаем .env файл # Загружаем .env файл
from dotenv import load_dotenv from dotenv import load_dotenv
env_path = os.path.join(project_root, '.env') env_path = os.path.join(project_root, '.env')
if os.path.exists(env_path): if os.path.exists(env_path):
load_dotenv(env_path) load_dotenv(env_path)

View File

@@ -3,8 +3,8 @@
Скрипт для диагностики и очистки проблем с голосовыми файлами Скрипт для диагностики и очистки проблем с голосовыми файлами
""" """
import asyncio import asyncio
import sys
import os import os
import sys
from pathlib import Path from pathlib import Path
# Добавляем корневую директорию проекта в путь # Добавляем корневую директорию проекта в путь

View File

@@ -1,11 +1,11 @@
import pytest
import asyncio import asyncio
import os import os
import sys import sys
from unittest.mock import Mock, AsyncMock, patch from unittest.mock import AsyncMock, Mock, patch
from aiogram.types import Message, User, Chat
from aiogram.fsm.context import FSMContext
import pytest
from aiogram.fsm.context import FSMContext
from aiogram.types import Chat, Message, User
from database.async_db import AsyncBotDB from database.async_db import AsyncBotDB
# Импортируем моки в самом начале # Импортируем моки в самом начале

View File

@@ -1,9 +1,10 @@
import pytest
import tempfile
import os import os
import tempfile
from datetime import datetime from datetime import datetime
from database.repositories.message_repository import MessageRepository
import pytest
from database.models import UserMessage from database.models import UserMessage
from database.repositories.message_repository import MessageRepository
@pytest.fixture(scope="session") @pytest.fixture(scope="session")

View File

@@ -1,11 +1,12 @@
import pytest
import asyncio import asyncio
import os import os
import tempfile import tempfile
from datetime import datetime from datetime import datetime
from unittest.mock import Mock, AsyncMock from unittest.mock import AsyncMock, Mock
import pytest
from database.models import MessageContentLink, PostContent, TelegramPost
from database.repositories.post_repository import PostRepository from database.repositories.post_repository import PostRepository
from database.models import TelegramPost, PostContent, MessageContentLink
@pytest.fixture(scope="session") @pytest.fixture(scope="session")

View File

@@ -1,10 +1,11 @@
""" """
Моки для тестового окружения Моки для тестового окружения
""" """
import sys
import os import os
import sys
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
# Патчим загрузку настроек до импорта модулей # Патчим загрузку настроек до импорта модулей
def setup_test_mocks(): def setup_test_mocks():
"""Настройка моков для тестов""" """Настройка моков для тестов"""

View File

@@ -1,10 +1,10 @@
import pytest
from unittest.mock import Mock, AsyncMock, patch, MagicMock
from datetime import datetime
import time import time
from datetime import datetime
from unittest.mock import AsyncMock, MagicMock, Mock, patch
from database.repositories.admin_repository import AdminRepository import pytest
from database.models import Admin from database.models import Admin
from database.repositories.admin_repository import AdminRepository
class TestAdminRepository: class TestAdminRepository:

View File

@@ -1,5 +1,6 @@
from unittest.mock import AsyncMock, Mock, patch
import pytest import pytest
from unittest.mock import Mock, AsyncMock, patch
from database.async_db import AsyncBotDB from database.async_db import AsyncBotDB

View File

@@ -1,10 +1,11 @@
import pytest
from unittest.mock import Mock, AsyncMock, patch, MagicMock, mock_open
from datetime import datetime
import time import time
from datetime import datetime
from unittest.mock import AsyncMock, MagicMock, Mock, mock_open, patch
import pytest
from helper_bot.handlers.voice.exceptions import (DatabaseError,
FileOperationError)
from helper_bot.handlers.voice.services import AudioFileService from helper_bot.handlers.voice.services import AudioFileService
from helper_bot.handlers.voice.exceptions import FileOperationError, DatabaseError
@pytest.fixture @pytest.fixture

View File

@@ -1,10 +1,10 @@
import pytest
from unittest.mock import Mock, AsyncMock, patch, MagicMock
from datetime import datetime
import time import time
from datetime import datetime
from unittest.mock import AsyncMock, MagicMock, Mock, patch
import pytest
from database.models import AudioListenRecord, AudioMessage, AudioModerate
from database.repositories.audio_repository import AudioRepository from database.repositories.audio_repository import AudioRepository
from database.models import AudioMessage, AudioListenRecord, AudioModerate
class TestAudioRepository: class TestAudioRepository:

View File

@@ -1,8 +1,8 @@
import pytest
from unittest.mock import Mock, AsyncMock, patch, MagicMock
from datetime import datetime
import time import time
from datetime import datetime
from unittest.mock import AsyncMock, MagicMock, Mock, patch
import pytest
from database.repositories.audio_repository import AudioRepository from database.repositories.audio_repository import AudioRepository

View File

@@ -1,9 +1,9 @@
import pytest
import sqlite3
import os import os
from datetime import datetime, timezone, timedelta import sqlite3
from unittest.mock import Mock, patch, AsyncMock from datetime import datetime, timedelta, timezone
from unittest.mock import AsyncMock, Mock, patch
import pytest
from helper_bot.utils.auto_unban_scheduler import AutoUnbanScheduler from helper_bot.utils.auto_unban_scheduler import AutoUnbanScheduler
@@ -155,8 +155,9 @@ class TestAutoUnbanIntegration:
} }
# Создаем реальный экземпляр базы данных с тестовым файлом # Создаем реальный экземпляр базы данных с тестовым файлом
from database.async_db import AsyncBotDB
import os import os
from database.async_db import AsyncBotDB
mock_factory.database = AsyncBotDB(test_db_path) mock_factory.database = AsyncBotDB(test_db_path)
return mock_factory return mock_factory

View File

@@ -1,9 +1,10 @@
import pytest
import asyncio import asyncio
from datetime import datetime, timezone, timedelta from datetime import datetime, timedelta, timezone
from unittest.mock import Mock, patch, AsyncMock from unittest.mock import AsyncMock, Mock, patch
from helper_bot.utils.auto_unban_scheduler import AutoUnbanScheduler, get_auto_unban_scheduler import pytest
from helper_bot.utils.auto_unban_scheduler import (AutoUnbanScheduler,
get_auto_unban_scheduler)
class TestAutoUnbanScheduler: class TestAutoUnbanScheduler:

View File

@@ -1,10 +1,11 @@
import pytest
from unittest.mock import Mock, AsyncMock, patch
from datetime import datetime
import time import time
from datetime import datetime
from unittest.mock import AsyncMock, Mock, patch
from database.repositories.blacklist_history_repository import BlacklistHistoryRepository import pytest
from database.models import BlacklistHistoryRecord from database.models import BlacklistHistoryRecord
from database.repositories.blacklist_history_repository import \
BlacklistHistoryRepository
class TestBlacklistHistoryRepository: class TestBlacklistHistoryRepository:

View File

@@ -1,10 +1,10 @@
import pytest
from unittest.mock import Mock, AsyncMock, patch, MagicMock
from datetime import datetime
import time import time
from datetime import datetime
from unittest.mock import AsyncMock, MagicMock, Mock, patch
from database.repositories.blacklist_repository import BlacklistRepository import pytest
from database.models import BlacklistUser from database.models import BlacklistUser
from database.repositories.blacklist_repository import BlacklistRepository
class TestBlacklistRepository: class TestBlacklistRepository:

View File

@@ -1,13 +1,11 @@
import pytest
from unittest.mock import Mock, AsyncMock, patch, MagicMock
from datetime import datetime
import time import time
from datetime import datetime
from unittest.mock import AsyncMock, MagicMock, Mock, patch
import pytest
from helper_bot.handlers.callback.callback_handlers import ( from helper_bot.handlers.callback.callback_handlers import (
save_voice_message, delete_voice_message, save_voice_message)
delete_voice_message from helper_bot.handlers.voice.constants import CALLBACK_DELETE, CALLBACK_SAVE
)
from helper_bot.handlers.voice.constants import CALLBACK_SAVE, CALLBACK_DELETE
@pytest.fixture @pytest.fixture

View File

@@ -2,18 +2,15 @@
Тесты для улучшенных методов обработки медиа Тесты для улучшенных методов обработки медиа
""" """
import pytest
import os import os
import tempfile import tempfile
from unittest.mock import Mock, AsyncMock, patch, MagicMock from unittest.mock import AsyncMock, MagicMock, Mock, patch
from aiogram import types
import pytest
from aiogram import types
from helper_bot.utils.helper_func import ( from helper_bot.utils.helper_func import (
download_file, add_in_db_media, add_in_db_media_mediagroup, download_file,
add_in_db_media, send_media_group_message_to_private_chat)
add_in_db_media_mediagroup,
send_media_group_message_to_private_chat
)
class TestDownloadFile: class TestDownloadFile:

View File

@@ -1,16 +1,15 @@
import pytest from unittest.mock import AsyncMock, Mock, patch
from unittest.mock import Mock, patch, AsyncMock
from aiogram.types import ReplyKeyboardMarkup, KeyboardButton, InlineKeyboardMarkup, InlineKeyboardButton
from helper_bot.keyboards.keyboards import ( import pytest
get_reply_keyboard, from aiogram.types import (InlineKeyboardButton, InlineKeyboardMarkup,
get_reply_keyboard_admin, KeyboardButton, ReplyKeyboardMarkup)
get_reply_keyboard_for_post,
get_reply_keyboard_leave_chat,
create_keyboard_with_pagination
)
from helper_bot.filters.main import ChatTypeFilter
from database.async_db import AsyncBotDB from database.async_db import AsyncBotDB
from helper_bot.filters.main import ChatTypeFilter
from helper_bot.keyboards.keyboards import (create_keyboard_with_pagination,
get_reply_keyboard,
get_reply_keyboard_admin,
get_reply_keyboard_for_post,
get_reply_keyboard_leave_chat)
class TestKeyboards: class TestKeyboards:

View File

@@ -1,9 +1,10 @@
import pytest
import asyncio import asyncio
from datetime import datetime from datetime import datetime
from unittest.mock import AsyncMock, MagicMock from unittest.mock import AsyncMock, MagicMock
from database.repositories.message_repository import MessageRepository
import pytest
from database.models import UserMessage from database.models import UserMessage
from database.repositories.message_repository import MessageRepository
class TestMessageRepository: class TestMessageRepository:

View File

@@ -1,10 +1,11 @@
import pytest
import asyncio import asyncio
import tempfile
import os import os
import tempfile
from datetime import datetime from datetime import datetime
from database.repositories.message_repository import MessageRepository
import pytest
from database.models import UserMessage from database.models import UserMessage
from database.repositories.message_repository import MessageRepository
class TestMessageRepositoryIntegration: class TestMessageRepositoryIntegration:

View File

@@ -1,9 +1,10 @@
import pytest
import asyncio import asyncio
from datetime import datetime from datetime import datetime
from unittest.mock import AsyncMock, MagicMock from unittest.mock import AsyncMock, MagicMock
import pytest
from database.models import MessageContentLink, PostContent, TelegramPost
from database.repositories.post_repository import PostRepository from database.repositories.post_repository import PostRepository
from database.models import TelegramPost, PostContent, MessageContentLink
class TestPostRepository: class TestPostRepository:

View File

@@ -1,10 +1,11 @@
import pytest
import asyncio import asyncio
import os import os
import tempfile import tempfile
from datetime import datetime from datetime import datetime
import pytest
from database.models import MessageContentLink, PostContent, TelegramPost
from database.repositories.post_repository import PostRepository from database.repositories.post_repository import PostRepository
from database.models import TelegramPost, PostContent, MessageContentLink
class TestPostRepositoryIntegration: class TestPostRepositoryIntegration:

View File

@@ -1,12 +1,12 @@
"""Tests for PostService""" """Tests for PostService"""
import pytest
from unittest.mock import Mock, AsyncMock, MagicMock, patch
from datetime import datetime from datetime import datetime
from aiogram import types from unittest.mock import AsyncMock, MagicMock, Mock, patch
from helper_bot.handlers.private.services import PostService, BotSettings import pytest
from aiogram import types
from database.models import TelegramPost, User from database.models import TelegramPost, User
from helper_bot.handlers.private.services import BotSettings, PostService
class TestPostService: class TestPostService:

View File

@@ -3,19 +3,18 @@
""" """
import asyncio import asyncio
import time import time
import pytest
from unittest.mock import AsyncMock, MagicMock, patch from unittest.mock import AsyncMock, MagicMock, patch
from helper_bot.utils.rate_limiter import ( import pytest
RateLimitConfig, from helper_bot.config.rate_limit_config import (RateLimitSettings,
ChatRateLimiter, get_rate_limit_config)
GlobalRateLimiter, from helper_bot.utils.rate_limit_monitor import (RateLimitMonitor,
RetryHandler, RateLimitStats,
TelegramRateLimiter, record_rate_limit_request)
send_with_rate_limit from helper_bot.utils.rate_limiter import (ChatRateLimiter, GlobalRateLimiter,
) RateLimitConfig, RetryHandler,
from helper_bot.utils.rate_limit_monitor import RateLimitMonitor, RateLimitStats, record_rate_limit_request TelegramRateLimiter,
from helper_bot.config.rate_limit_config import RateLimitSettings, get_rate_limit_config send_with_rate_limit)
class TestRateLimitConfig: class TestRateLimitConfig:

View File

@@ -1,14 +1,12 @@
from unittest.mock import AsyncMock, Mock, patch
import pytest import pytest
from unittest.mock import Mock, AsyncMock, patch
from aiogram import types from aiogram import types
from aiogram.fsm.context import FSMContext from aiogram.fsm.context import FSMContext
from helper_bot.handlers.admin.exceptions import (InvalidInputError,
from helper_bot.handlers.admin.services import AdminService, User, BannedUser UserAlreadyBannedError,
from helper_bot.handlers.admin.exceptions import ( UserNotFoundError)
UserNotFoundError, from helper_bot.handlers.admin.services import AdminService, BannedUser, User
UserAlreadyBannedError,
InvalidInputError
)
class TestAdminService: class TestAdminService:

View File

@@ -1,16 +1,16 @@
"""Tests for refactored group handlers""" """Tests for refactored group handlers"""
from unittest.mock import AsyncMock, MagicMock, Mock
import pytest import pytest
from unittest.mock import Mock, AsyncMock, MagicMock
from aiogram import types from aiogram import types
from aiogram.fsm.context import FSMContext from aiogram.fsm.context import FSMContext
from helper_bot.handlers.group.constants import ERROR_MESSAGES, FSM_STATES
from helper_bot.handlers.group.group_handlers import ( from helper_bot.handlers.group.exceptions import (NoReplyToMessageError,
create_group_handlers, GroupHandlers UserNotFoundError)
) from helper_bot.handlers.group.group_handlers import (GroupHandlers,
create_group_handlers)
from helper_bot.handlers.group.services import AdminReplyService from helper_bot.handlers.group.services import AdminReplyService
from helper_bot.handlers.group.exceptions import NoReplyToMessageError, UserNotFoundError
from helper_bot.handlers.group.constants import FSM_STATES, ERROR_MESSAGES
class TestGroupHandlers: class TestGroupHandlers:

View File

@@ -1,15 +1,14 @@
"""Tests for refactored private handlers""" """Tests for refactored private handlers"""
from unittest.mock import AsyncMock, MagicMock, Mock
import pytest import pytest
from unittest.mock import Mock, AsyncMock, MagicMock
from aiogram import types from aiogram import types
from aiogram.fsm.context import FSMContext from aiogram.fsm.context import FSMContext
from helper_bot.handlers.private.constants import BUTTON_TEXTS, FSM_STATES
from helper_bot.handlers.private.private_handlers import ( from helper_bot.handlers.private.private_handlers import (
create_private_handlers, PrivateHandlers PrivateHandlers, create_private_handlers)
)
from helper_bot.handlers.private.services import BotSettings from helper_bot.handlers.private.services import BotSettings
from helper_bot.handlers.private.constants import FSM_STATES, BUTTON_TEXTS
class TestPrivateHandlers: class TestPrivateHandlers:

View File

@@ -1,39 +1,24 @@
import pytest
from unittest.mock import Mock, patch, AsyncMock
from datetime import datetime
import os import os
from datetime import datetime
from unittest.mock import AsyncMock, Mock, patch
from helper_bot.utils.helper_func import ( import helper_bot.utils.messages as messages # Import for patching constants
get_first_name, import pytest
get_text_message,
determine_anonymity,
check_username_and_full_name,
safe_html_escape,
download_file,
prepare_media_group_from_middlewares,
add_in_db_media_mediagroup,
add_in_db_media,
send_media_group_message_to_private_chat,
send_media_group_to_channel,
send_text_message,
send_photo_message,
send_video_message,
send_video_note_message,
send_audio_message,
send_voice_message,
check_access,
add_days_to_date,
get_banned_users_list,
get_banned_users_buttons,
delete_user_blacklist,
update_user_info,
check_user_emoji,
get_random_emoji
)
from helper_bot.utils.messages import get_message
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory, get_global_instance
from database.async_db import AsyncBotDB from database.async_db import AsyncBotDB
import helper_bot.utils.messages as messages # Import for patching constants from helper_bot.utils.base_dependency_factory import (BaseDependencyFactory,
get_global_instance)
from helper_bot.utils.helper_func import (
add_days_to_date, add_in_db_media, add_in_db_media_mediagroup,
check_access, check_user_emoji, check_username_and_full_name,
delete_user_blacklist, determine_anonymity, download_file,
get_banned_users_buttons, get_banned_users_list, get_first_name,
get_random_emoji, get_text_message, prepare_media_group_from_middlewares,
safe_html_escape, send_audio_message,
send_media_group_message_to_private_chat, send_media_group_to_channel,
send_photo_message, send_text_message, send_video_message,
send_video_note_message, send_voice_message, update_user_info)
from helper_bot.utils.messages import get_message
class TestHelperFunctions: class TestHelperFunctions:
"""Тесты для вспомогательных функций""" """Тесты для вспомогательных функций"""

View File

@@ -1,11 +1,14 @@
import pytest
from unittest.mock import Mock, AsyncMock, patch
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from unittest.mock import AsyncMock, Mock, patch
import pytest
from helper_bot.handlers.voice.exceptions import (AudioProcessingError,
VoiceMessageError)
from helper_bot.handlers.voice.services import VoiceBotService from helper_bot.handlers.voice.services import VoiceBotService
from helper_bot.handlers.voice.exceptions import VoiceMessageError, AudioProcessingError from helper_bot.handlers.voice.utils import (get_last_message_text,
from helper_bot.handlers.voice.utils import get_last_message_text, validate_voice_message, get_user_emoji_safe get_user_emoji_safe,
validate_voice_message)
class TestVoiceBotService: class TestVoiceBotService:

View File

@@ -1,21 +1,14 @@
import pytest import pytest
from helper_bot.handlers.voice.constants import ( from helper_bot.handlers.voice.constants import (BTN_LISTEN, BTN_SPEAK,
BUTTON_COMMAND_MAPPING, BUTTON_COMMAND_MAPPING,
COMMAND_MAPPING, CALLBACK_COMMAND_MAPPING,
CALLBACK_COMMAND_MAPPING, CALLBACK_DELETE,
VOICE_BOT_NAME, CALLBACK_SAVE, CMD_EMOJI,
STATE_START, CMD_HELP, CMD_REFRESH,
STATE_STANDUP_WRITE, CMD_RESTART, CMD_START,
BTN_SPEAK, COMMAND_MAPPING,
BTN_LISTEN, STATE_STANDUP_WRITE,
CMD_START, STATE_START, VOICE_BOT_NAME)
CMD_HELP,
CMD_RESTART,
CMD_EMOJI,
CMD_REFRESH,
CALLBACK_SAVE,
CALLBACK_DELETE
)
class TestVoiceConstants: class TestVoiceConstants:

View File

@@ -1,9 +1,7 @@
import pytest import pytest
from helper_bot.handlers.voice.exceptions import ( from helper_bot.handlers.voice.exceptions import (AudioProcessingError,
VoiceMessageError, VoiceBotError,
AudioProcessingError, VoiceMessageError)
VoiceBotError
)
class TestVoiceExceptions: class TestVoiceExceptions:

View File

@@ -1,10 +1,11 @@
from unittest.mock import AsyncMock, MagicMock, Mock, patch
import pytest import pytest
from unittest.mock import Mock, AsyncMock, patch, MagicMock
from aiogram import types from aiogram import types
from aiogram.fsm.context import FSMContext from aiogram.fsm.context import FSMContext
from helper_bot.handlers.voice.constants import (STATE_STANDUP_WRITE,
STATE_START)
from helper_bot.handlers.voice.voice_handler import VoiceHandlers from helper_bot.handlers.voice.voice_handler import VoiceHandlers
from helper_bot.handlers.voice.constants import STATE_START, STATE_STANDUP_WRITE
class TestVoiceHandler: class TestVoiceHandler:

View File

@@ -1,10 +1,11 @@
import pytest
from unittest.mock import Mock, AsyncMock, patch, MagicMock
from pathlib import Path
from datetime import datetime from datetime import datetime
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock, Mock, patch
import pytest
from helper_bot.handlers.voice.exceptions import (AudioProcessingError,
VoiceMessageError)
from helper_bot.handlers.voice.services import VoiceBotService from helper_bot.handlers.voice.services import VoiceBotService
from helper_bot.handlers.voice.exceptions import VoiceMessageError, AudioProcessingError
class TestVoiceBotService: class TestVoiceBotService:

View File

@@ -1,15 +1,12 @@
import pytest
from unittest.mock import Mock, patch
from datetime import datetime, timedelta from datetime import datetime, timedelta
from aiogram import types from unittest.mock import Mock, patch
from helper_bot.handlers.voice.utils import ( import pytest
get_last_message_text, from aiogram import types
validate_voice_message, from helper_bot.handlers.voice.utils import (format_time_ago,
get_user_emoji_safe, get_last_message_text,
format_time_ago, get_user_emoji_safe, plural_time,
plural_time validate_voice_message)
)
class TestVoiceUtils: class TestVoiceUtils:
@@ -120,7 +117,7 @@ class TestVoiceUtils:
def test_format_time_ago_minutes(self): def test_format_time_ago_minutes(self):
"""Тест форматирования времени в минутах""" """Тест форматирования времени в минутах"""
from datetime import datetime, timedelta from datetime import datetime, timedelta
# Создаем дату 30 минут назад # Создаем дату 30 минут назад
test_date = (datetime.now() - timedelta(minutes=30)).strftime("%Y-%m-%d %H:%M:%S") test_date = (datetime.now() - timedelta(minutes=30)).strftime("%Y-%m-%d %H:%M:%S")
@@ -133,7 +130,7 @@ class TestVoiceUtils:
def test_format_time_ago_hours(self): def test_format_time_ago_hours(self):
"""Тест форматирования времени в часах""" """Тест форматирования времени в часах"""
from datetime import datetime, timedelta from datetime import datetime, timedelta
# Создаем дату 2 часа назад # Создаем дату 2 часа назад
test_date = (datetime.now() - timedelta(hours=2)).strftime("%Y-%m-%d %H:%M:%S") test_date = (datetime.now() - timedelta(hours=2)).strftime("%Y-%m-%d %H:%M:%S")
@@ -145,7 +142,7 @@ class TestVoiceUtils:
def test_format_time_ago_days(self): def test_format_time_ago_days(self):
"""Тест форматирования времени в днях""" """Тест форматирования времени в днях"""
from datetime import datetime, timedelta from datetime import datetime, timedelta
# Создаем дату 3 дня назад # Создаем дату 3 дня назад
test_date = (datetime.now() - timedelta(days=3)).strftime("%Y-%m-%d %H:%M:%S") test_date = (datetime.now() - timedelta(days=3)).strftime("%Y-%m-%d %H:%M:%S")