Refactor project structure and enhance Docker support
- Removed unnecessary `__init__.py` and `Dockerfile` to streamline project organization. - Updated `.dockerignore` and `.gitignore` to improve exclusion patterns for build artifacts and environment files. - Enhanced `Makefile` with new commands for managing Docker containers and added help documentation. - Introduced `pyproject.toml` for better project metadata management and dependency tracking. - Updated `requirements.txt` to reflect changes in dependencies for metrics and monitoring. - Refactored various handler files to improve code organization and maintainability.
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
from typing import Annotated, Dict, Any
|
||||
from typing import Dict, Any
|
||||
try:
|
||||
from typing import Annotated
|
||||
except ImportError:
|
||||
from typing_extensions import Annotated
|
||||
from aiogram import BaseMiddleware
|
||||
from aiogram.types import TelegramObject
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import html
|
||||
from tkinter import S
|
||||
import traceback
|
||||
|
||||
from aiogram import Router
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
"""Constants for group handlers"""
|
||||
|
||||
from typing import Final
|
||||
from typing import Final, Dict
|
||||
|
||||
# FSM States
|
||||
FSM_STATES: Final[dict[str, str]] = {
|
||||
FSM_STATES: Final[Dict[str, str]] = {
|
||||
"CHAT": "CHAT"
|
||||
}
|
||||
|
||||
# Error messages
|
||||
ERROR_MESSAGES: Final[dict[str, str]] = {
|
||||
ERROR_MESSAGES: Final[Dict[str, str]] = {
|
||||
"NO_REPLY_TO_MESSAGE": "Блять, выдели сообщение!",
|
||||
"USER_NOT_FOUND": "Не могу найти кому ответить в базе, проебали сообщение."
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"""Constants for private handlers"""
|
||||
|
||||
from typing import Final
|
||||
from typing import Final, Dict
|
||||
|
||||
# FSM States
|
||||
FSM_STATES: Final[dict[str, str]] = {
|
||||
FSM_STATES: Final[Dict[str, str]] = {
|
||||
"START": "START",
|
||||
"SUGGEST": "SUGGEST",
|
||||
"PRE_CHAT": "PRE_CHAT",
|
||||
@@ -11,7 +11,7 @@ FSM_STATES: Final[dict[str, str]] = {
|
||||
}
|
||||
|
||||
# Button texts
|
||||
BUTTON_TEXTS: Final[dict[str, str]] = {
|
||||
BUTTON_TEXTS: Final[Dict[str, str]] = {
|
||||
"SUGGEST_POST": "📢Предложить свой пост",
|
||||
"SAY_GOODBYE": "👋🏼Сказать пока!",
|
||||
"LEAVE_CHAT": "Выйти из чата",
|
||||
@@ -21,7 +21,7 @@ BUTTON_TEXTS: Final[dict[str, str]] = {
|
||||
}
|
||||
|
||||
# Error messages
|
||||
ERROR_MESSAGES: Final[dict[str, str]] = {
|
||||
ERROR_MESSAGES: Final[Dict[str, str]] = {
|
||||
"UNSUPPORTED_CONTENT": (
|
||||
'Я пока не умею работать с таким сообщением. '
|
||||
'Пришли текст и фото/фоты(ы). А лучше перешли это сообщение админу @kerrad1\n'
|
||||
|
||||
@@ -24,6 +24,14 @@ from helper_bot.utils.helper_func import (
|
||||
check_user_emoji
|
||||
)
|
||||
|
||||
# Local imports - metrics
|
||||
from helper_bot.utils.metrics import (
|
||||
metrics,
|
||||
track_time,
|
||||
track_errors,
|
||||
db_query_time
|
||||
)
|
||||
|
||||
# Local imports - modular components
|
||||
from .constants import FSM_STATES, BUTTON_TEXTS, ERROR_MESSAGES
|
||||
from .services import BotSettings, UserService, PostService, StickerService
|
||||
@@ -91,16 +99,23 @@ 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"""
|
||||
"""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)
|
||||
await state.set_state(FSM_STATES["START"])
|
||||
|
||||
# Send sticker
|
||||
# Send sticker with metrics
|
||||
await self.sticker_service.send_random_hello_sticker(message)
|
||||
|
||||
# Send welcome message
|
||||
# Send welcome message with metrics
|
||||
markup = get_reply_keyboard(self.db, message.from_user.id)
|
||||
hello_message = messages.get_message(get_first_name(message), 'HELLO_MESSAGE')
|
||||
await message.answer(hello_message, reply_markup=markup, parse_mode='HTML')
|
||||
|
||||
@@ -30,6 +30,14 @@ from helper_bot.utils.helper_func import (
|
||||
)
|
||||
from helper_bot.keyboards import get_reply_keyboard_for_post
|
||||
|
||||
# Local imports - metrics
|
||||
from helper_bot.utils.metrics import (
|
||||
metrics,
|
||||
track_time,
|
||||
track_errors,
|
||||
db_query_time
|
||||
)
|
||||
|
||||
|
||||
class DatabaseProtocol(Protocol):
|
||||
"""Protocol for database operations"""
|
||||
@@ -65,13 +73,18 @@ class UserService:
|
||||
self.db = db
|
||||
self.settings = settings
|
||||
|
||||
@track_time("update_user_activity", "user_service")
|
||||
@track_errors("user_service", "update_user_activity")
|
||||
@db_query_time("update_user_activity", "users", "update")
|
||||
async def update_user_activity(self, user_id: int) -> None:
|
||||
"""Update user's last activity timestamp"""
|
||||
"""Update user's last activity timestamp with metrics tracking"""
|
||||
current_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
self.db.update_date_for_user(current_date, user_id)
|
||||
|
||||
@track_time("ensure_user_exists", "user_service")
|
||||
@track_errors("user_service", "ensure_user_exists")
|
||||
async def ensure_user_exists(self, message: types.Message) -> None:
|
||||
"""Ensure user exists in database, create if needed"""
|
||||
"""Ensure user exists in database, create if needed with metrics tracking"""
|
||||
user_id = message.from_user.id
|
||||
full_name = message.from_user.full_name
|
||||
username = message.from_user.username or "private_username"
|
||||
@@ -82,14 +95,17 @@ class UserService:
|
||||
current_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
if not self.db.user_exists(user_id):
|
||||
# Record database operation
|
||||
self.db.add_new_user_in_db(
|
||||
user_id, first_name, full_name, username, is_bot, language_code,
|
||||
"", current_date, current_date
|
||||
)
|
||||
metrics.record_db_query("add_new_user", 0.0, "users", "insert")
|
||||
else:
|
||||
is_need_update = check_username_and_full_name(user_id, username, full_name, self.db)
|
||||
if is_need_update:
|
||||
self.db.update_username_and_full_name(user_id, username, full_name)
|
||||
metrics.record_db_query("update_username_fullname", 0.0, "users", "update")
|
||||
safe_full_name = html.escape(full_name) if full_name else "Неизвестный пользователь"
|
||||
safe_username = html.escape(username) if username else "Без никнейма"
|
||||
|
||||
@@ -100,9 +116,12 @@ class UserService:
|
||||
text=f'Для пользователя: {user_id} обновлены данные в БД.\nНовое имя: {safe_full_name}\nНовый ник:{safe_username}')
|
||||
|
||||
self.db.update_date_for_user(current_date, user_id)
|
||||
metrics.record_db_query("update_date_for_user", 0.0, "users", "update")
|
||||
|
||||
@track_time("log_user_message", "user_service")
|
||||
@track_errors("user_service", "log_user_message")
|
||||
async def log_user_message(self, message: types.Message) -> None:
|
||||
"""Forward user message to logs group"""
|
||||
"""Forward user message to logs group with metrics tracking"""
|
||||
await message.forward(chat_id=self.settings.group_for_logs)
|
||||
|
||||
def get_safe_user_info(self, message: types.Message) -> tuple[str, str]:
|
||||
@@ -210,7 +229,7 @@ class PostService:
|
||||
message_id=media_group_message_id, helper_message_id=help_message_id
|
||||
)
|
||||
|
||||
async def process_post(self, message: types.Message, album: Union[list[types.Message], None] = None) -> None:
|
||||
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)
|
||||
|
||||
@@ -248,8 +267,10 @@ class StickerService:
|
||||
def __init__(self, settings: BotSettings) -> None:
|
||||
self.settings = settings
|
||||
|
||||
@track_time("send_random_hello_sticker", "sticker_service")
|
||||
@track_errors("sticker_service", "send_random_hello_sticker")
|
||||
async def send_random_hello_sticker(self, message: types.Message) -> None:
|
||||
"""Send random hello sticker"""
|
||||
"""Send random hello sticker with metrics tracking"""
|
||||
name_stick_hello = list(Path('Stick').rglob('Hello_*'))
|
||||
if not name_stick_hello:
|
||||
return
|
||||
@@ -258,8 +279,10 @@ class StickerService:
|
||||
await message.answer_sticker(random_stick_hello)
|
||||
await asyncio.sleep(0.3)
|
||||
|
||||
@track_time("send_random_goodbye_sticker", "sticker_service")
|
||||
@track_errors("sticker_service", "send_random_goodbye_sticker")
|
||||
async def send_random_goodbye_sticker(self, message: types.Message) -> None:
|
||||
"""Send random goodbye sticker"""
|
||||
"""Send random goodbye sticker with metrics tracking"""
|
||||
name_stick_bye = list(Path('Stick').rglob('Universal_*'))
|
||||
if not name_stick_bye:
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user