Dev 6 #9
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,6 +2,7 @@
|
||||
/database/tg-bot-database.db
|
||||
/database/tg-bot-database.db-shm
|
||||
/database/tg-bot-database.db-wm
|
||||
/database/tg-bot-database.db-wal
|
||||
/database/test.db
|
||||
/database/test.db-shm
|
||||
/database/test.db-wal
|
||||
|
||||
13
Makefile
13
Makefile
@@ -36,6 +36,19 @@ restart: ## Перезапустить все сервисы (с пересбо
|
||||
docker-compose build
|
||||
docker-compose up -d
|
||||
|
||||
restart-bot: ## Перезапустить только бота
|
||||
docker-compose stop telegram-bot
|
||||
docker-compose build telegram-bot
|
||||
docker-compose up -d telegram-bot
|
||||
|
||||
restart-prometheus: ## Перезапустить только Prometheus
|
||||
docker-compose stop prometheus
|
||||
docker-compose up -d prometheus
|
||||
|
||||
restart-grafana: ## Перезапустить только Grafana
|
||||
docker-compose stop grafana
|
||||
docker-compose up -d grafana
|
||||
|
||||
status: ## Показать статус контейнеров
|
||||
docker-compose ps
|
||||
|
||||
|
||||
@@ -284,7 +284,7 @@
|
||||
"type": "prometheus",
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"expr": "rate(errors_total[5m])",
|
||||
"expr": "sum(rate(errors_total[5m]))",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
@@ -371,7 +371,7 @@
|
||||
"type": "prometheus",
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"expr": "active_users",
|
||||
"expr": "sum(active_users)",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
"""
|
||||
Examples of how to use metrics decorators in your bot handlers.
|
||||
These examples show how to integrate metrics without modifying existing logic.
|
||||
"""
|
||||
|
||||
from aiogram import Router, F
|
||||
from aiogram.types import Message, CallbackQuery
|
||||
from aiogram.filters import Command
|
||||
from aiogram.fsm.context import FSMContext
|
||||
|
||||
# Import metrics decorators
|
||||
from ..utils.metrics import track_time, track_errors, db_query_time, metrics
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
# Example 1: Basic command handler with timing and error tracking
|
||||
@router.message(Command("start"))
|
||||
@track_time("start_command", "private_handler")
|
||||
@track_errors("private_handler", "start_command")
|
||||
async def start_command(message: Message, state: FSMContext):
|
||||
"""Start command handler with metrics."""
|
||||
# Your existing logic here
|
||||
await message.answer("Welcome! Bot started.")
|
||||
|
||||
# Optionally record custom metrics
|
||||
metrics.record_command("start", "private_handler", "user")
|
||||
|
||||
|
||||
# Example 2: Group command handler with custom labels
|
||||
@router.message(Command("help"), F.chat.type.in_({"group", "supergroup"}))
|
||||
@track_time("help_command", "group_handler")
|
||||
@track_errors("group_handler", "help_command")
|
||||
async def help_command(message: Message):
|
||||
"""Help command handler for groups."""
|
||||
await message.answer("Group help information.")
|
||||
|
||||
# Record command with group context
|
||||
metrics.record_command("help", "group_handler", "group_user")
|
||||
|
||||
|
||||
# Example 3: Callback handler with timing
|
||||
@router.callback_query(F.data.startswith("menu:"))
|
||||
@track_time("menu_callback", "callback_handler")
|
||||
@track_errors("callback_handler", "menu_callback")
|
||||
async def menu_callback(callback: CallbackQuery):
|
||||
"""Menu callback handler."""
|
||||
data = callback.data
|
||||
await callback.answer(f"Menu: {data}")
|
||||
|
||||
# Record callback processing
|
||||
metrics.record_message("callback_query", "callback", "callback_handler")
|
||||
|
||||
|
||||
# Example 4: Database operation with query timing
|
||||
@db_query_time("user_lookup", "users", "select")
|
||||
async def get_user_info(user_id: int):
|
||||
"""Example database function with timing."""
|
||||
# Your database query here
|
||||
# result = await db.fetch_one("SELECT * FROM users WHERE id = ?", user_id)
|
||||
return {"user_id": user_id, "status": "active"}
|
||||
|
||||
|
||||
# Example 5: Complex handler with multiple metrics
|
||||
@router.message(Command("stats"))
|
||||
@track_time("stats_command", "admin_handler")
|
||||
@track_errors("admin_handler", "stats_command")
|
||||
async def stats_command(message: Message):
|
||||
"""Stats command with detailed metrics."""
|
||||
try:
|
||||
# Record command execution
|
||||
metrics.record_command("stats", "admin_handler", "admin_user")
|
||||
|
||||
# Your stats logic here
|
||||
stats = await get_bot_stats()
|
||||
|
||||
# Record successful execution
|
||||
await message.answer(f"Bot stats: {stats}")
|
||||
|
||||
except Exception as e:
|
||||
# Error is automatically tracked by decorator
|
||||
await message.answer("Error getting stats")
|
||||
raise
|
||||
|
||||
|
||||
# Example 6: Message handler with message type tracking
|
||||
@router.message()
|
||||
@track_time("message_processing", "general_handler")
|
||||
async def handle_message(message: Message):
|
||||
"""General message handler."""
|
||||
# Message type is automatically detected by middleware
|
||||
# But you can add custom tracking
|
||||
|
||||
if message.photo:
|
||||
# Custom metric for photo processing
|
||||
metrics.record_message("photo", "general", "photo_handler")
|
||||
|
||||
# Your message handling logic
|
||||
await message.answer("Message received")
|
||||
|
||||
|
||||
# Example 7: Error-prone operation with custom error tracking
|
||||
@track_errors("file_handler", "file_upload")
|
||||
async def upload_file(file_data: bytes, filename: str):
|
||||
"""File upload with error tracking."""
|
||||
try:
|
||||
# Your file upload logic
|
||||
# result = await upload_service.upload(file_data, filename)
|
||||
return {"status": "success", "filename": filename}
|
||||
|
||||
except Exception as e:
|
||||
# Custom error metric
|
||||
metrics.record_error(
|
||||
type(e).__name__,
|
||||
"file_handler",
|
||||
"file_upload"
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
# Example 8: Background task with metrics
|
||||
async def background_metrics_collection():
|
||||
"""Background task for collecting periodic metrics."""
|
||||
while True:
|
||||
try:
|
||||
# Collect custom metrics
|
||||
active_users = await count_active_users()
|
||||
metrics.set_active_users(active_users, "current")
|
||||
|
||||
# Wait before next collection
|
||||
await asyncio.sleep(300) # 5 minutes
|
||||
|
||||
except Exception as e:
|
||||
metrics.record_error(
|
||||
type(e).__name__,
|
||||
"background_task",
|
||||
"metrics_collection"
|
||||
)
|
||||
await asyncio.sleep(60) # Wait 1 minute on error
|
||||
|
||||
|
||||
# Example 9: Custom metric collection in service
|
||||
class UserService:
|
||||
"""Example service with integrated metrics."""
|
||||
|
||||
@db_query_time("user_creation", "users", "insert")
|
||||
async def create_user(self, user_data: dict):
|
||||
"""Create user with database timing."""
|
||||
# Your user creation logic
|
||||
# user_id = await self.db.execute("INSERT INTO users ...")
|
||||
return {"user_id": 123, "status": "created"}
|
||||
|
||||
@track_time("user_update", "user_service")
|
||||
async def update_user(self, user_id: int, updates: dict):
|
||||
"""Update user with timing."""
|
||||
# Your update logic
|
||||
# await self.db.execute("UPDATE users SET ...")
|
||||
return {"user_id": user_id, "status": "updated"}
|
||||
|
||||
|
||||
# Example 10: Middleware integration example
|
||||
async def custom_middleware(handler, event, data):
|
||||
"""Custom middleware that works with metrics system."""
|
||||
from ..utils.metrics import track_middleware
|
||||
|
||||
async with track_middleware("custom_middleware"):
|
||||
# Your middleware logic
|
||||
result = await handler(event, data)
|
||||
return result
|
||||
|
||||
|
||||
# Helper function for stats (placeholder)
|
||||
async def get_bot_stats():
|
||||
"""Get bot statistics."""
|
||||
return {
|
||||
"total_users": 1000,
|
||||
"active_today": 150,
|
||||
"commands_processed": 5000
|
||||
}
|
||||
|
||||
|
||||
# Helper function for user counting (placeholder)
|
||||
async def count_active_users():
|
||||
"""Count active users."""
|
||||
return 150
|
||||
|
||||
|
||||
# Import asyncio for background task
|
||||
import asyncio
|
||||
@@ -16,6 +16,12 @@ from .exceptions import UserNotFoundError
|
||||
# Local imports - utilities
|
||||
from logs.custom_logger import logger
|
||||
|
||||
# Local imports - metrics
|
||||
from helper_bot.utils.metrics import (
|
||||
metrics,
|
||||
track_time,
|
||||
track_errors
|
||||
)
|
||||
|
||||
class GroupHandlers:
|
||||
"""Main handler class for group messages"""
|
||||
@@ -41,6 +47,7 @@ class GroupHandlers:
|
||||
@error_handler
|
||||
async def handle_message(self, message: types.Message, state: FSMContext):
|
||||
"""Handle admin reply to user through group chat"""
|
||||
|
||||
logger.info(
|
||||
f'Получено сообщение в группе {message.chat.title} (ID: {message.chat.id}) '
|
||||
f'от пользователя {message.from_user.full_name} (ID: {message.from_user.id}): "{message.text}"'
|
||||
|
||||
@@ -11,6 +11,14 @@ from helper_bot.utils.helper_func import send_text_message
|
||||
from .exceptions import NoReplyToMessageError, UserNotFoundError
|
||||
from logs.custom_logger import logger
|
||||
|
||||
# Local imports - metrics
|
||||
from helper_bot.utils.metrics import (
|
||||
metrics,
|
||||
track_time,
|
||||
track_errors,
|
||||
db_query_time
|
||||
)
|
||||
|
||||
|
||||
class DatabaseProtocol(Protocol):
|
||||
"""Protocol for database operations"""
|
||||
|
||||
@@ -28,8 +28,7 @@ from helper_bot.utils.helper_func import (
|
||||
from helper_bot.utils.metrics import (
|
||||
metrics,
|
||||
track_time,
|
||||
track_errors,
|
||||
db_query_time
|
||||
track_errors
|
||||
)
|
||||
|
||||
# Local imports - modular components
|
||||
@@ -99,14 +98,8 @@ class PrivateHandlers:
|
||||
await message.answer('Я перезапущен!', reply_markup=markup, parse_mode='HTML')
|
||||
|
||||
@error_handler
|
||||
@track_time("start_message_handler", "private_handler")
|
||||
@track_errors("private_handler", "start_message_handler")
|
||||
async def handle_start_message(self, message: types.Message, state: FSMContext, **kwargs):
|
||||
"""Handle start command and return to bot button with metrics tracking"""
|
||||
# Record start command metrics
|
||||
metrics.record_command("start", "private_handler", "user" if not message.from_user.is_bot else "bot")
|
||||
metrics.record_message("command", "private", "private_handler")
|
||||
|
||||
# User service operations with metrics
|
||||
await self.user_service.log_user_message(message)
|
||||
await self.user_service.ensure_user_exists(message)
|
||||
@@ -123,6 +116,7 @@ class PrivateHandlers:
|
||||
@error_handler
|
||||
async def suggest_post(self, message: types.Message, state: FSMContext, **kwargs):
|
||||
"""Handle suggest post button"""
|
||||
# User service operations with metrics
|
||||
await self.user_service.update_user_activity(message.from_user.id)
|
||||
await self.user_service.log_user_message(message)
|
||||
await state.set_state(FSM_STATES["SUGGEST"])
|
||||
@@ -134,6 +128,7 @@ class PrivateHandlers:
|
||||
@error_handler
|
||||
async def end_message(self, message: types.Message, state: FSMContext, **kwargs):
|
||||
"""Handle goodbye button"""
|
||||
# User service operations with metrics
|
||||
await self.user_service.update_user_activity(message.from_user.id)
|
||||
await self.user_service.log_user_message(message)
|
||||
|
||||
@@ -149,6 +144,7 @@ class PrivateHandlers:
|
||||
@error_handler
|
||||
async def suggest_router(self, message: types.Message, state: FSMContext, album: list = None, **kwargs):
|
||||
"""Handle post submission in suggest state"""
|
||||
# Post service operations with metrics
|
||||
await self.post_service.process_post(message, album)
|
||||
|
||||
# Send success message and return to start state
|
||||
@@ -160,6 +156,7 @@ class PrivateHandlers:
|
||||
@error_handler
|
||||
async def stickers(self, message: types.Message, state: FSMContext, **kwargs):
|
||||
"""Handle stickers request"""
|
||||
# User service operations with metrics
|
||||
markup = get_reply_keyboard(self.db, message.from_user.id)
|
||||
self.db.update_info_about_stickers(user_id=message.from_user.id)
|
||||
await self.user_service.log_user_message(message)
|
||||
@@ -172,6 +169,7 @@ class PrivateHandlers:
|
||||
@error_handler
|
||||
async def connect_with_admin(self, message: types.Message, state: FSMContext, **kwargs):
|
||||
"""Handle connect with admin button"""
|
||||
# User service operations with metrics
|
||||
await self.user_service.update_user_activity(message.from_user.id)
|
||||
admin_message = messages.get_message(get_first_name(message), 'CONNECT_WITH_ADMIN')
|
||||
await message.answer(admin_message, parse_mode="html")
|
||||
@@ -181,6 +179,7 @@ class PrivateHandlers:
|
||||
@error_handler
|
||||
async def resend_message_in_group_for_message(self, message: types.Message, state: FSMContext, **kwargs):
|
||||
"""Handle messages in admin chat states"""
|
||||
# User service operations with metrics
|
||||
await self.user_service.update_user_activity(message.from_user.id)
|
||||
await message.forward(chat_id=self.settings.group_for_message)
|
||||
|
||||
|
||||
@@ -138,6 +138,8 @@ class PostService:
|
||||
self.db = db
|
||||
self.settings = settings
|
||||
|
||||
@track_time("handle_text_post", "post_service")
|
||||
@track_errors("post_service", "handle_text_post")
|
||||
async def handle_text_post(self, message: types.Message, first_name: str) -> None:
|
||||
"""Handle text post submission"""
|
||||
post_text = get_text_message(message.text.lower(), first_name, message.from_user.username)
|
||||
@@ -146,6 +148,8 @@ class PostService:
|
||||
sent_message_id = await send_text_message(self.settings.group_for_posts, message, post_text, markup)
|
||||
self.db.add_post_in_db(sent_message_id, message.text, message.from_user.id)
|
||||
|
||||
@track_time("handle_photo_post", "post_service")
|
||||
@track_errors("post_service", "handle_photo_post")
|
||||
async def handle_photo_post(self, message: types.Message, first_name: str) -> None:
|
||||
"""Handle photo post submission"""
|
||||
post_caption = ""
|
||||
@@ -160,6 +164,8 @@ class PostService:
|
||||
self.db.add_post_in_db(sent_message.message_id, sent_message.caption, message.from_user.id)
|
||||
await add_in_db_media(sent_message, self.db)
|
||||
|
||||
@track_time("handle_video_post", "post_service")
|
||||
@track_errors("post_service", "handle_video_post")
|
||||
async def handle_video_post(self, message: types.Message, first_name: str) -> None:
|
||||
"""Handle video post submission"""
|
||||
post_caption = ""
|
||||
@@ -174,6 +180,8 @@ class PostService:
|
||||
self.db.add_post_in_db(sent_message.message_id, sent_message.caption, message.from_user.id)
|
||||
await add_in_db_media(sent_message, self.db)
|
||||
|
||||
@track_time("handle_video_note_post", "post_service")
|
||||
@track_errors("post_service", "handle_video_note_post")
|
||||
async def handle_video_note_post(self, message: types.Message) -> None:
|
||||
"""Handle video note post submission"""
|
||||
markup = get_reply_keyboard_for_post()
|
||||
@@ -184,6 +192,8 @@ class PostService:
|
||||
self.db.add_post_in_db(sent_message.message_id, sent_message.caption, message.from_user.id)
|
||||
await add_in_db_media(sent_message, self.db)
|
||||
|
||||
@track_time("handle_audio_post", "post_service")
|
||||
@track_errors("post_service", "handle_audio_post")
|
||||
async def handle_audio_post(self, message: types.Message, first_name: str) -> None:
|
||||
"""Handle audio post submission"""
|
||||
post_caption = ""
|
||||
@@ -198,6 +208,8 @@ class PostService:
|
||||
self.db.add_post_in_db(sent_message.message_id, sent_message.caption, message.from_user.id)
|
||||
await add_in_db_media(sent_message, self.db)
|
||||
|
||||
@track_time("handle_voice_post", "post_service")
|
||||
@track_errors("post_service", "handle_voice_post")
|
||||
async def handle_voice_post(self, message: types.Message) -> None:
|
||||
"""Handle voice post submission"""
|
||||
markup = get_reply_keyboard_for_post()
|
||||
@@ -208,6 +220,8 @@ class PostService:
|
||||
self.db.add_post_in_db(sent_message.message_id, sent_message.caption, message.from_user.id)
|
||||
await add_in_db_media(sent_message, self.db)
|
||||
|
||||
@track_time("handle_media_group_post", "post_service")
|
||||
@track_errors("post_service", "handle_media_group_post")
|
||||
async def handle_media_group_post(self, message: types.Message, album: list, first_name: str) -> None:
|
||||
"""Handle media group post submission"""
|
||||
post_caption = " "
|
||||
@@ -229,6 +243,8 @@ class PostService:
|
||||
message_id=media_group_message_id, helper_message_id=help_message_id
|
||||
)
|
||||
|
||||
@track_time("process_post", "post_service")
|
||||
@track_errors("post_service", "process_post")
|
||||
async def process_post(self, message: types.Message, album: Union[list, None] = None) -> None:
|
||||
"""Process post based on content type"""
|
||||
first_name = get_first_name(message)
|
||||
|
||||
@@ -17,17 +17,13 @@ async def start_bot(bdf):
|
||||
bot = Bot(token=token, default=DefaultBotProperties(
|
||||
parse_mode='HTML',
|
||||
link_preview_is_disabled=bdf.settings['Telegram']['preview_link']
|
||||
), timeout=30.0) # Добавляем таймаут для предотвращения зависаний
|
||||
), timeout=30.0)
|
||||
dp = Dispatcher(storage=MemoryStorage(), fsm_strategy=FSMStrategy.GLOBAL_USER)
|
||||
|
||||
# ✅ Middleware для метрик (добавляем первыми)
|
||||
dp.message.middleware(MetricsMiddleware())
|
||||
dp.callback_query.middleware(MetricsMiddleware())
|
||||
dp.message.middleware(ErrorMetricsMiddleware())
|
||||
dp.callback_query.middleware(ErrorMetricsMiddleware())
|
||||
|
||||
# ✅ Глобальная middleware для всех роутеров
|
||||
# ✅ Оптимизированная регистрация middleware
|
||||
dp.update.outer_middleware(DependenciesMiddleware())
|
||||
dp.update.outer_middleware(MetricsMiddleware())
|
||||
dp.update.outer_middleware(BlacklistMiddleware())
|
||||
|
||||
dp.include_routers(admin_router, private_router, callback_router, group_router)
|
||||
await bot.delete_webhook(drop_pending_updates=True)
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
"""
|
||||
Example integration of metrics monitoring in the main bot file.
|
||||
This shows how to integrate the metrics system without modifying existing handlers.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from aiogram import Bot, Dispatcher
|
||||
from aiogram.enums import ParseMode
|
||||
from aiogram.fsm.storage.memory import MemoryStorage
|
||||
|
||||
# Import metrics components
|
||||
from .utils.metrics import metrics
|
||||
from .utils.metrics_exporter import MetricsManager
|
||||
from .middlewares.metrics_middleware import MetricsMiddleware, ErrorMetricsMiddleware
|
||||
|
||||
# Import your existing bot components
|
||||
# from .handlers import ... # Your existing handlers
|
||||
# from .database.db import BotDB # Your existing database class
|
||||
|
||||
|
||||
class BotWithMetrics:
|
||||
"""Bot class with integrated metrics monitoring."""
|
||||
|
||||
def __init__(self, token: str, metrics_port: int = 8000):
|
||||
self.bot = Bot(token=token, parse_mode=ParseMode.HTML)
|
||||
self.storage = MemoryStorage()
|
||||
self.dp = Dispatcher(storage=self.storage)
|
||||
|
||||
# Initialize metrics manager
|
||||
# You can pass your database instance here if needed
|
||||
# self.metrics_manager = MetricsManager(port=metrics_port, db=your_db_instance)
|
||||
self.metrics_manager = MetricsManager(port=metrics_port)
|
||||
|
||||
# Setup middlewares
|
||||
self._setup_middlewares()
|
||||
|
||||
# Setup handlers (your existing handlers)
|
||||
# self._setup_handlers()
|
||||
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
def _setup_middlewares(self):
|
||||
"""Setup metrics middlewares."""
|
||||
# Add metrics middleware first to capture all events
|
||||
self.dp.message.middleware(MetricsMiddleware())
|
||||
self.dp.callback_query.middleware(MetricsMiddleware())
|
||||
|
||||
# Add error tracking middleware
|
||||
self.dp.message.middleware(ErrorMetricsMiddleware())
|
||||
self.dp.callback_query.middleware(ErrorMetricsMiddleware())
|
||||
|
||||
# Your existing middlewares can go here
|
||||
# self.dp.message.middleware(YourExistingMiddleware())
|
||||
|
||||
def _setup_handlers(self):
|
||||
"""Setup bot handlers."""
|
||||
# Import and register your existing handlers here
|
||||
# from .handlers.admin import admin_router
|
||||
# from .handlers.private import private_router
|
||||
# from .handlers.group import group_router
|
||||
# from .handlers.callback import callback_router
|
||||
#
|
||||
# self.dp.include_router(admin_router)
|
||||
# self.dp.include_router(private_router)
|
||||
# self.dp.include_router(group_router)
|
||||
# self.dp.include_router(callback_router)
|
||||
pass
|
||||
|
||||
async def start(self):
|
||||
"""Start the bot with metrics."""
|
||||
try:
|
||||
# Start metrics collection
|
||||
await self.metrics_manager.start()
|
||||
self.logger.info("Metrics system started")
|
||||
|
||||
# Start bot polling
|
||||
await self.dp.start_polling(self.bot)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error starting bot: {e}")
|
||||
raise
|
||||
finally:
|
||||
await self.stop()
|
||||
|
||||
async def stop(self):
|
||||
"""Stop the bot and metrics."""
|
||||
try:
|
||||
# Stop metrics collection
|
||||
await self.metrics_manager.stop()
|
||||
self.logger.info("Metrics system stopped")
|
||||
|
||||
# Stop bot
|
||||
await self.bot.session.close()
|
||||
self.logger.info("Bot stopped")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error stopping bot: {e}")
|
||||
|
||||
|
||||
# Example usage function
|
||||
async def main():
|
||||
"""Main function to run the bot with metrics."""
|
||||
# Your bot token
|
||||
TOKEN = "YOUR_BOT_TOKEN_HERE"
|
||||
|
||||
# Create and start bot
|
||||
bot = BotWithMetrics(TOKEN)
|
||||
|
||||
try:
|
||||
await bot.start()
|
||||
except KeyboardInterrupt:
|
||||
await bot.stop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -2,6 +2,7 @@ from typing import Dict, Any
|
||||
import html
|
||||
|
||||
from aiogram import BaseMiddleware, types
|
||||
from aiogram.types import TelegramObject, Message, CallbackQuery
|
||||
from helper_bot.utils.base_dependency_factory import get_global_instance
|
||||
from logs.custom_logger import logger
|
||||
|
||||
@@ -10,17 +11,38 @@ BotDB = bdf.get_db()
|
||||
|
||||
|
||||
class BlacklistMiddleware(BaseMiddleware):
|
||||
async def __call__(self, handler, event: types.Message, data: Dict[str, Any]) -> Any:
|
||||
logger.info(f'Вызов BlacklistMiddleware для пользователя {event.from_user.username}')
|
||||
async def __call__(self, handler, event: TelegramObject, data: Dict[str, Any]) -> Any:
|
||||
# Проверяем тип события и получаем пользователя
|
||||
user = None
|
||||
if isinstance(event, Message):
|
||||
user = event.from_user
|
||||
elif isinstance(event, CallbackQuery):
|
||||
user = event.from_user
|
||||
|
||||
# Если это не сообщение или callback, пропускаем проверку
|
||||
if not user:
|
||||
return await handler(event, data)
|
||||
|
||||
logger.info(f'Вызов BlacklistMiddleware для пользователя {user.username}')
|
||||
|
||||
# Используем асинхронную версию для предотвращения блокировки
|
||||
if await BotDB.check_user_in_blacklist_async(user_id=event.from_user.id):
|
||||
logger.info(f'BlacklistMiddleware результат для пользователя: {event.from_user.username} заблокирован!')
|
||||
user_info = await BotDB.get_blacklist_users_by_id_async(event.from_user.id)
|
||||
if await BotDB.check_user_in_blacklist_async(user_id=user.id):
|
||||
logger.info(f'BlacklistMiddleware результат для пользователя: {user.username} заблокирован!')
|
||||
user_info = await BotDB.get_blacklist_users_by_id_async(user.id)
|
||||
# Экранируем потенциально проблемные символы
|
||||
reason = html.escape(str(user_info[2])) if user_info[2] else "Не указана"
|
||||
date_unban = html.escape(str(user_info[3])) if user_info[3] else "Не указана"
|
||||
|
||||
# Отправляем сообщение в зависимости от типа события
|
||||
if isinstance(event, Message):
|
||||
await event.answer(
|
||||
f"<b>Ты заблокирован.</b>\n<b>Причина блокировки:</b> {reason}\n<b>Дата разбана:</b> {date_unban}")
|
||||
elif isinstance(event, CallbackQuery):
|
||||
await event.answer(
|
||||
f"<b>Ты заблокирован.</b>\n<b>Причина блокировки:</b> {reason}\n<b>Дата разбана:</b> {date_unban}",
|
||||
show_alert=True)
|
||||
|
||||
return False
|
||||
logger.info(f'BlacklistMiddleware результат для пользователя: {event.from_user.username} доступ разрешен')
|
||||
|
||||
logger.info(f'BlacklistMiddleware результат для пользователя: {user.username} доступ разрешен')
|
||||
return await handler(event, data)
|
||||
|
||||
@@ -8,7 +8,7 @@ from aiogram import BaseMiddleware
|
||||
from aiogram.types import TelegramObject, Message, CallbackQuery
|
||||
from aiogram.enums import ChatType
|
||||
import time
|
||||
from ..utils.metrics import metrics, track_middleware
|
||||
from ..utils.metrics import metrics
|
||||
|
||||
|
||||
class MetricsMiddleware(BaseMiddleware):
|
||||
@@ -22,21 +22,20 @@ class MetricsMiddleware(BaseMiddleware):
|
||||
) -> Any:
|
||||
"""Process event and collect metrics."""
|
||||
|
||||
async with track_middleware("metrics_middleware"):
|
||||
# Record message processing
|
||||
# Record basic event metrics
|
||||
if isinstance(event, Message):
|
||||
await self._record_message_metrics(event, data)
|
||||
await self._record_message_metrics(event)
|
||||
elif isinstance(event, CallbackQuery):
|
||||
await self._record_callback_metrics(event, data)
|
||||
await self._record_callback_metrics(event)
|
||||
|
||||
# Execute handler and collect timing
|
||||
# Execute handler with timing
|
||||
start_time = time.time()
|
||||
try:
|
||||
result = await handler(event, data)
|
||||
duration = time.time() - start_time
|
||||
|
||||
# Record successful execution
|
||||
handler_name = handler.__name__ if hasattr(handler, '__name__') else "unknown"
|
||||
handler_name = self._get_handler_name(handler)
|
||||
metrics.record_method_duration(
|
||||
handler_name,
|
||||
duration,
|
||||
@@ -50,7 +49,7 @@ class MetricsMiddleware(BaseMiddleware):
|
||||
duration = time.time() - start_time
|
||||
|
||||
# Record error and timing
|
||||
handler_name = handler.__name__ if hasattr(handler, '__name__') else "unknown"
|
||||
handler_name = self._get_handler_name(handler)
|
||||
metrics.record_method_duration(
|
||||
handler_name,
|
||||
duration,
|
||||
@@ -64,8 +63,16 @@ class MetricsMiddleware(BaseMiddleware):
|
||||
)
|
||||
raise
|
||||
|
||||
async def _record_message_metrics(self, message: Message, data: Dict[str, Any]):
|
||||
"""Record metrics for message processing."""
|
||||
def _get_handler_name(self, handler: Callable) -> str:
|
||||
"""Extract handler name efficiently."""
|
||||
if hasattr(handler, '__name__'):
|
||||
return handler.__name__
|
||||
elif hasattr(handler, '__qualname__'):
|
||||
return handler.__qualname__
|
||||
return "unknown"
|
||||
|
||||
async def _record_message_metrics(self, message: Message):
|
||||
"""Record message metrics efficiently."""
|
||||
# Determine message type
|
||||
message_type = "text"
|
||||
if message.photo:
|
||||
@@ -92,41 +99,25 @@ class MetricsMiddleware(BaseMiddleware):
|
||||
elif message.chat.type == ChatType.CHANNEL:
|
||||
chat_type = "channel"
|
||||
|
||||
# Determine handler type
|
||||
handler_type = "unknown"
|
||||
if message.text and message.text.startswith('/'):
|
||||
handler_type = "command"
|
||||
# Record command specifically
|
||||
command = message.text.split()[0][1:] # Remove '/' and get command name
|
||||
metrics.record_command(
|
||||
command,
|
||||
"message_handler",
|
||||
"user" if message.from_user else "unknown"
|
||||
)
|
||||
|
||||
# Record message processing
|
||||
metrics.record_message(message_type, chat_type, handler_type)
|
||||
metrics.record_message(message_type, chat_type, "message_handler")
|
||||
|
||||
async def _record_callback_metrics(self, callback: CallbackQuery, data: Dict[str, Any]):
|
||||
"""Record metrics for callback query processing."""
|
||||
# Record callback processing
|
||||
metrics.record_message(
|
||||
"callback_query",
|
||||
"callback",
|
||||
"callback_handler"
|
||||
)
|
||||
# Record command if applicable
|
||||
if message.text and message.text.startswith('/'):
|
||||
command = message.text.split()[0][1:] # Remove '/' and get command name
|
||||
user_type = "user" if message.from_user else "unknown"
|
||||
metrics.record_command(command, "message_handler", user_type)
|
||||
|
||||
async def _record_callback_metrics(self, callback: CallbackQuery):
|
||||
"""Record callback metrics efficiently."""
|
||||
metrics.record_message("callback_query", "callback", "callback_handler")
|
||||
|
||||
# Record callback command if available
|
||||
if callback.data:
|
||||
# Extract command from callback data (assuming format like "command:param")
|
||||
parts = callback.data.split(':', 1)
|
||||
if parts:
|
||||
command = parts[0]
|
||||
metrics.record_command(
|
||||
command,
|
||||
"callback_handler",
|
||||
"user" if callback.from_user else "unknown"
|
||||
)
|
||||
user_type = "user" if callback.from_user else "unknown"
|
||||
metrics.record_command(command, "callback_handler", user_type)
|
||||
|
||||
|
||||
class DatabaseMetricsMiddleware(BaseMiddleware):
|
||||
|
||||
@@ -31,7 +31,8 @@ class BotMetrics:
|
||||
'method_duration_seconds',
|
||||
'Time spent executing methods',
|
||||
['method_name', 'handler_type', 'status'],
|
||||
buckets=[0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0],
|
||||
# Оптимизированные buckets для Telegram API (обычно < 1 сек)
|
||||
buckets=[0.01, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0],
|
||||
registry=self.registry
|
||||
)
|
||||
|
||||
@@ -56,7 +57,8 @@ class BotMetrics:
|
||||
'db_query_duration_seconds',
|
||||
'Time spent executing database queries',
|
||||
['query_type', 'table_name', 'operation'],
|
||||
buckets=[0.01, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5],
|
||||
# Оптимизированные buckets для SQLite/PostgreSQL
|
||||
buckets=[0.001, 0.005, 0.01, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5],
|
||||
registry=self.registry
|
||||
)
|
||||
|
||||
@@ -73,7 +75,16 @@ class BotMetrics:
|
||||
'middleware_duration_seconds',
|
||||
'Time spent in middleware execution',
|
||||
['middleware_name', 'status'],
|
||||
buckets=[0.01, 0.05, 0.1, 0.25, 0.5],
|
||||
# Middleware должен быть быстрым
|
||||
buckets=[0.001, 0.005, 0.01, 0.05, 0.1, 0.25],
|
||||
registry=self.registry
|
||||
)
|
||||
|
||||
# Rate limiting metrics
|
||||
self.rate_limit_hits_total = Counter(
|
||||
'rate_limit_hits_total',
|
||||
'Total number of rate limit hits',
|
||||
['limit_type', 'handler_type'],
|
||||
registry=self.registry
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user