feat: добавлена система миграций БД и CI/CD пайплайны

- Создана система отслеживания миграций (MigrationRepository, таблица migrations)
- Добавлен скрипт apply_migrations.py для автоматического применения миграций
- Созданы CI/CD пайплайны (.github/workflows/ci.yml, deploy.yml)
- Обновлена документация по миграциям в database-patterns.md
- Миграции применяются автоматически при деплое в продакшн
This commit is contained in:
2026-01-25 23:17:09 +03:00
parent 07e72c4d14
commit e2b1353408
109 changed files with 1342 additions and 1441 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,10 +1,10 @@
import html
from typing import Optional
from aiogram import types
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.keyboards.keyboards import get_reply_keyboard_admin
from logs.custom_logger import logger

View File

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

View File

@@ -1,37 +1,34 @@
import html
import traceback
import time
import traceback
from datetime import datetime
from aiogram import Router, F
from aiogram.types import CallbackQuery
from aiogram.fsm.context import FSMContext
from aiogram import F, Router
from aiogram.filters import MagicData
from helper_bot.handlers.voice.constants import CALLBACK_SAVE, CALLBACK_DELETE
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 aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery
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 .dependency_factory import get_post_publish_service, get_ban_service
from .exceptions import UserBlockedBotError, PostNotFoundError, UserNotFoundError, PublishError, BanError
from .constants import (
CALLBACK_PUBLISH, CALLBACK_DECLINE, CALLBACK_BAN, CALLBACK_UNLOCK,
CALLBACK_RETURN, CALLBACK_PAGE, MESSAGE_PUBLISHED, MESSAGE_DECLINED,
MESSAGE_USER_BANNED, MESSAGE_USER_UNLOCKED, MESSAGE_ERROR,
ERROR_BOT_BLOCKED
)
from helper_bot.utils.helper_func import (get_banned_users_buttons,
get_banned_users_list)
# Local imports - metrics
from helper_bot.utils.metrics import (db_query_time, track_errors,
track_file_operations, track_time)
from logs.custom_logger import logger
# Local imports - metrics
from helper_bot.utils.metrics import (
track_time,
track_errors,
db_query_time,
track_file_operations
)
from .constants import (CALLBACK_BAN, CALLBACK_DECLINE, CALLBACK_PAGE,
CALLBACK_PUBLISH, CALLBACK_RETURN, CALLBACK_UNLOCK,
ERROR_BOT_BLOCKED, MESSAGE_DECLINED, MESSAGE_ERROR,
MESSAGE_PUBLISHED, MESSAGE_USER_BANNED,
MESSAGE_USER_UNLOCKED)
from .dependency_factory import get_ban_service, get_post_publish_service
from .exceptions import (BanError, PostNotFoundError, PublishError,
UserBlockedBotError, UserNotFoundError)
callback_router = Router()

View File

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

View File

@@ -1,10 +1,11 @@
from typing import Callable
from aiogram import Bot
from aiogram.client.default import DefaultBotProperties
from aiogram.fsm.context import FSMContext
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:

View File

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

View File

@@ -1,28 +1,13 @@
"""Group handlers package for Telegram bot"""
# 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
from .constants import (
FSM_STATES,
ERROR_MESSAGES
)
from .exceptions import (
NoReplyToMessageError,
UserNotFoundError
)
from .constants import ERROR_MESSAGES, FSM_STATES
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__ = [
# Main components

View File

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

View File

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

View File

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

View File

@@ -1,22 +1,17 @@
"""Service classes for group handlers"""
# Standard library imports
from typing import Protocol, Optional
from typing import Optional, Protocol
# Third-party imports
from aiogram import types
# Local imports
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
# Local imports - metrics
from helper_bot.utils.metrics import (
track_time,
track_errors,
db_query_time
)
from .exceptions import NoReplyToMessageError, UserNotFoundError
class DatabaseProtocol(Protocol):

View File

@@ -1,27 +1,13 @@
"""Private handlers package for Telegram bot"""
# 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
from .constants import (
FSM_STATES,
BUTTON_TEXTS,
ERROR_MESSAGES
)
from .constants import BUTTON_TEXTS, ERROR_MESSAGES, FSM_STATES
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__ = [
# Main components

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,16 +1,12 @@
import time
import html
import time
from datetime import datetime
from typing import Optional
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 helper_bot.utils.metrics import (
track_time,
track_errors,
db_query_time
)
def format_time_ago(date_from_db: str) -> Optional[str]:
"""Форматировать время с момента последней записи"""

View File

@@ -2,33 +2,31 @@ import asyncio
from datetime import datetime
from pathlib import Path
from aiogram import Router, types, F
from aiogram.filters import Command, StateFilter, MagicData
from aiogram import F, Router, types
from aiogram.filters import Command, MagicData, StateFilter
from aiogram.fsm.context import FSMContext
from aiogram.types import FSInputFile
from helper_bot.filters.main import ChatTypeFilter
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 get_first_name, update_user_info, check_user_emoji, send_voice_message
from logs.custom_logger import logger
from helper_bot.handlers.private.constants import BUTTON_TEXTS, FSM_STATES
from helper_bot.handlers.voice.constants import *
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.keyboards.keyboards import get_main_keyboard, get_reply_keyboard_for_voice
from helper_bot.handlers.voice.utils import (get_last_message_text,
get_user_emoji_safe,
validate_voice_message)
from helper_bot.keyboards import get_reply_keyboard
from helper_bot.handlers.private.constants import FSM_STATES
from helper_bot.handlers.private.constants import BUTTON_TEXTS
from helper_bot.keyboards.keyboards import (get_main_keyboard,
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
from helper_bot.utils.metrics import (
track_time,
track_errors,
db_query_time,
track_file_operations
)
from helper_bot.utils.metrics import (db_query_time, track_errors,
track_file_operations, track_time)
from logs.custom_logger import logger
class VoiceHandlers:
def __init__(self, db, settings):