Add middleware and refactor admin handlers for improved functionality
- Introduced `DependenciesMiddleware` and `BlacklistMiddleware` for enhanced request handling across all routers. - Refactored admin handlers to utilize new middleware, improving access control and error handling. - Updated the `admin_router` to include middleware for access checks and streamlined the process of banning users. - Enhanced the structure of admin handler imports for better organization and maintainability. - Improved error handling in various admin functions to ensure robust user interactions.
This commit is contained in:
@@ -1,20 +1,45 @@
|
||||
"""Private handlers package for Telegram bot"""
|
||||
|
||||
from .private_handlers import private_router, create_private_handlers, PrivateHandlers
|
||||
from .services import BotSettings, UserService, PostService, StickerService
|
||||
from .constants import FSM_STATES, BUTTON_TEXTS, ERROR_MESSAGES
|
||||
# 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 .decorators import error_handler
|
||||
|
||||
__all__ = [
|
||||
# Main components
|
||||
'private_router',
|
||||
'create_private_handlers',
|
||||
'PrivateHandlers',
|
||||
|
||||
# Services
|
||||
'BotSettings',
|
||||
'UserService',
|
||||
'PostService',
|
||||
'StickerService',
|
||||
|
||||
# Constants
|
||||
'FSM_STATES',
|
||||
'BUTTON_TEXTS',
|
||||
'ERROR_MESSAGES',
|
||||
|
||||
# Utilities
|
||||
'error_handler'
|
||||
]
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
"""Constants for private handlers"""
|
||||
|
||||
from typing import Final
|
||||
|
||||
# FSM States
|
||||
FSM_STATES = {
|
||||
FSM_STATES: Final[dict[str, str]] = {
|
||||
"START": "START",
|
||||
"SUGGEST": "SUGGEST",
|
||||
"PRE_CHAT": "PRE_CHAT",
|
||||
@@ -9,7 +11,7 @@ FSM_STATES = {
|
||||
}
|
||||
|
||||
# Button texts
|
||||
BUTTON_TEXTS = {
|
||||
BUTTON_TEXTS: Final[dict[str, str]] = {
|
||||
"SUGGEST_POST": "📢Предложить свой пост",
|
||||
"SAY_GOODBYE": "👋🏼Сказать пока!",
|
||||
"LEAVE_CHAT": "Выйти из чата",
|
||||
@@ -19,7 +21,7 @@ BUTTON_TEXTS = {
|
||||
}
|
||||
|
||||
# Error messages
|
||||
ERROR_MESSAGES = {
|
||||
ERROR_MESSAGES: Final[dict[str, str]] = {
|
||||
"UNSUPPORTED_CONTENT": (
|
||||
'Я пока не умею работать с таким сообщением. '
|
||||
'Пришли текст и фото/фоты(ы). А лучше перешли это сообщение админу @kerrad1\n'
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
"""Decorators and utility functions for private handlers"""
|
||||
|
||||
# Standard library imports
|
||||
import traceback
|
||||
from typing import Any, Callable
|
||||
|
||||
# Third-party imports
|
||||
from aiogram import types
|
||||
|
||||
# Local imports
|
||||
from logs.custom_logger import logger
|
||||
|
||||
|
||||
def error_handler(func):
|
||||
def error_handler(func: Callable[..., Any]) -> Callable[..., Any]:
|
||||
"""Decorator for centralized error handling"""
|
||||
async def wrapper(*args, **kwargs):
|
||||
async def wrapper(*args: Any, **kwargs: Any) -> Any:
|
||||
try:
|
||||
return await func(*args, **kwargs)
|
||||
except Exception as e:
|
||||
@@ -23,7 +29,8 @@ def error_handler(func):
|
||||
chat_id=important_logs,
|
||||
text=f"Произошла ошибка в {func.__name__}: {str(e)}\n\nTraceback:\n{traceback.format_exc()}"
|
||||
)
|
||||
except:
|
||||
pass # If we can't log the error, at least it was logged to logger
|
||||
except Exception:
|
||||
# If we can't log the error, at least it was logged to logger
|
||||
pass
|
||||
raise
|
||||
return wrapper
|
||||
|
||||
@@ -1,28 +1,30 @@
|
||||
import random
|
||||
import traceback
|
||||
import asyncio
|
||||
import html
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
"""Main private handlers module for Telegram bot"""
|
||||
|
||||
# Standard library imports
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
|
||||
# Third-party imports
|
||||
from aiogram import types, Router, F
|
||||
from aiogram.filters import Command, StateFilter
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.types import FSInputFile
|
||||
|
||||
# Local imports - filters and middlewares
|
||||
from helper_bot.filters.main import ChatTypeFilter
|
||||
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
|
||||
from helper_bot.utils import messages
|
||||
from helper_bot.utils.helper_func import get_first_name, get_text_message, send_text_message, send_photo_message, \
|
||||
send_media_group_message_to_private_chat, prepare_media_group_from_middlewares, send_video_message, \
|
||||
send_video_note_message, send_audio_message, send_voice_message, add_in_db_media, \
|
||||
check_user_emoji, check_username_and_full_name, update_user_info
|
||||
from logs.custom_logger import logger
|
||||
|
||||
# Import new modular components
|
||||
# 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
|
||||
)
|
||||
|
||||
# Local imports - modular components
|
||||
from .constants import FSM_STATES, BUTTON_TEXTS, ERROR_MESSAGES
|
||||
from .services import BotSettings, UserService, PostService, StickerService
|
||||
from .decorators import error_handler
|
||||
@@ -112,10 +114,7 @@ class PrivateHandlers:
|
||||
|
||||
markup = types.ReplyKeyboardRemove()
|
||||
suggest_news = messages.get_message(get_first_name(message), 'SUGGEST_NEWS')
|
||||
await message.answer(suggest_news)
|
||||
await asyncio.sleep(0.3)
|
||||
suggest_news_2 = messages.get_message(get_first_name(message), 'SUGGEST_NEWS_2')
|
||||
await message.answer(suggest_news_2, reply_markup=markup)
|
||||
await message.answer(suggest_news, reply_markup=markup)
|
||||
|
||||
@error_handler
|
||||
async def end_message(self, message: types.Message, state: FSMContext, **kwargs):
|
||||
|
||||
@@ -1,25 +1,50 @@
|
||||
"""Service classes for private handlers"""
|
||||
|
||||
# Standard library imports
|
||||
import random
|
||||
import asyncio
|
||||
import html
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Dict, Callable
|
||||
from typing import Dict, Callable, Any, Protocol, Union
|
||||
from dataclasses import dataclass
|
||||
|
||||
# Third-party imports
|
||||
from aiogram import types
|
||||
from aiogram.types import FSInputFile
|
||||
|
||||
# Local imports - utilities
|
||||
from helper_bot.utils.helper_func import (
|
||||
get_first_name, get_text_message, send_text_message, send_photo_message,
|
||||
send_media_group_message_to_private_chat, prepare_media_group_from_middlewares,
|
||||
send_video_message, send_video_note_message, send_audio_message, send_voice_message,
|
||||
add_in_db_media, check_username_and_full_name
|
||||
get_first_name,
|
||||
get_text_message,
|
||||
send_text_message,
|
||||
send_photo_message,
|
||||
send_media_group_message_to_private_chat,
|
||||
prepare_media_group_from_middlewares,
|
||||
send_video_message,
|
||||
send_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
|
||||
|
||||
|
||||
class DatabaseProtocol(Protocol):
|
||||
"""Protocol for database operations"""
|
||||
def user_exists(self, user_id: int) -> bool: ...
|
||||
def add_new_user_in_db(self, user_id: int, first_name: str, full_name: str,
|
||||
username: str, is_bot: bool, language_code: str,
|
||||
emoji: str, created_date: str, updated_date: str) -> None: ...
|
||||
def update_username_and_full_name(self, user_id: int, username: str, full_name: str) -> None: ...
|
||||
def update_date_for_user(self, date: str, user_id: int) -> None: ...
|
||||
def add_post_in_db(self, message_id: int, text: str, user_id: int) -> None: ...
|
||||
def update_info_about_stickers(self, user_id: int) -> None: ...
|
||||
def add_new_message_in_db(self, text: str, user_id: int, message_id: int, date: str) -> None: ...
|
||||
def update_helper_message_in_db(self, message_id: int, helper_message_id: int) -> None: ...
|
||||
|
||||
|
||||
@dataclass
|
||||
class BotSettings:
|
||||
"""Bot configuration settings"""
|
||||
@@ -36,7 +61,7 @@ class BotSettings:
|
||||
class UserService:
|
||||
"""Service for user-related operations"""
|
||||
|
||||
def __init__(self, db, settings: BotSettings):
|
||||
def __init__(self, db: DatabaseProtocol, settings: BotSettings) -> None:
|
||||
self.db = db
|
||||
self.settings = settings
|
||||
|
||||
@@ -90,7 +115,7 @@ class UserService:
|
||||
class PostService:
|
||||
"""Service for post-related operations"""
|
||||
|
||||
def __init__(self, db, settings: BotSettings):
|
||||
def __init__(self, db: DatabaseProtocol, settings: BotSettings) -> None:
|
||||
self.db = db
|
||||
self.settings = settings
|
||||
|
||||
@@ -168,7 +193,7 @@ class PostService:
|
||||
"""Handle media group post submission"""
|
||||
post_caption = " "
|
||||
|
||||
if album[0].caption:
|
||||
if album and album[0].caption:
|
||||
post_caption = get_text_message(album[0].caption.lower(), first_name, message.from_user.username)
|
||||
|
||||
media_group = await prepare_media_group_from_middlewares(album, post_caption)
|
||||
@@ -185,7 +210,7 @@ class PostService:
|
||||
message_id=media_group_message_id, helper_message_id=help_message_id
|
||||
)
|
||||
|
||||
async def process_post(self, message: types.Message, album: list = None) -> None:
|
||||
async def process_post(self, message: types.Message, album: Union[list[types.Message], None] = None) -> None:
|
||||
"""Process post based on content type"""
|
||||
first_name = get_first_name(message)
|
||||
|
||||
@@ -220,12 +245,14 @@ class PostService:
|
||||
class StickerService:
|
||||
"""Service for sticker-related operations"""
|
||||
|
||||
def __init__(self, settings: BotSettings):
|
||||
def __init__(self, settings: BotSettings) -> None:
|
||||
self.settings = settings
|
||||
|
||||
async def send_random_hello_sticker(self, message: types.Message) -> None:
|
||||
"""Send random hello sticker"""
|
||||
name_stick_hello = list(Path('Stick').rglob('Hello_*'))
|
||||
if not name_stick_hello:
|
||||
return
|
||||
random_stick_hello = random.choice(name_stick_hello)
|
||||
random_stick_hello = FSInputFile(path=random_stick_hello)
|
||||
await message.answer_sticker(random_stick_hello)
|
||||
@@ -234,6 +261,8 @@ class StickerService:
|
||||
async def send_random_goodbye_sticker(self, message: types.Message) -> None:
|
||||
"""Send random goodbye sticker"""
|
||||
name_stick_bye = list(Path('Stick').rglob('Universal_*'))
|
||||
if not name_stick_bye:
|
||||
return
|
||||
random_stick_bye = random.choice(name_stick_bye)
|
||||
random_stick_bye = FSInputFile(path=random_stick_bye)
|
||||
await message.answer_sticker(random_stick_bye)
|
||||
|
||||
Reference in New Issue
Block a user