Refactor Docker and configuration files for improved structure and functionality

- Updated `.dockerignore` to include additional development and temporary files, enhancing build efficiency.
- Modified `.gitignore` to remove unnecessary entries and streamline ignored files.
- Enhanced `docker-compose.yml` with health checks, resource limits, and improved environment variable handling for better service management.
- Refactored `Dockerfile.bot` to utilize a multi-stage build for optimized image size and security.
- Improved `Makefile` with new commands for deployment, migration, and backup, along with enhanced help documentation.
- Updated `requirements.txt` to include new dependencies for environment variable management.
- Refactored metrics handling in the bot to ensure proper initialization and collection.
This commit is contained in:
2025-08-29 23:15:06 +03:00
parent f097d69dd4
commit 8f338196b7
27 changed files with 1499 additions and 370 deletions

View File

@@ -0,0 +1,91 @@
"""
Configuration management for the Telegram bot.
Supports both environment variables and .env files.
"""
import os
from typing import Dict, Any, Optional
from dotenv import load_dotenv
class ConfigManager:
"""Manages bot configuration with environment variable support."""
def __init__(self, env_file: str = ".env"):
self.env_file = env_file
self._load_env()
def _load_env(self):
"""Load configuration from .env file if exists."""
# Load from .env file if exists
if os.path.exists(self.env_file):
load_dotenv(self.env_file)
def get(self, section: str, key: str, default: Any = None) -> str:
"""Get configuration value with environment variable override."""
# Check environment variable first
env_key = f"{section.upper()}_{key.upper()}"
env_value = os.getenv(env_key)
if env_value is not None:
return env_value
# Fall back to direct environment variable
direct_env_value = os.getenv(key.upper())
if direct_env_value is not None:
return direct_env_value
return default
def getboolean(self, section: str, key: str, default: bool = False) -> bool:
"""Get boolean configuration value."""
value = self.get(section, key, str(default))
if isinstance(value, bool):
return value
return value.lower() in ('true', '1', 'yes', 'on')
def getint(self, section: str, key: str, default: int = 0) -> int:
"""Get integer configuration value."""
value = self.get(section, key, str(default))
try:
return int(value)
except (ValueError, TypeError):
return default
def get_all_settings(self) -> Dict[str, Dict[str, Any]]:
"""Get all settings as dictionary."""
settings = {}
# Telegram секция
settings['Telegram'] = {
'bot_token': self.get('Telegram', 'bot_token', ''),
'listen_bot_token': self.get('Telegram', 'listen_bot_token', ''),
'test_bot_token': self.get('Telegram', 'test_bot_token', ''),
'preview_link': self.getboolean('Telegram', 'preview_link', False),
'main_public': self.get('Telegram', 'main_public', ''),
'group_for_posts': self.getint('Telegram', 'group_for_posts', 0),
'group_for_message': self.getint('Telegram', 'group_for_message', 0),
'group_for_logs': self.getint('Telegram', 'group_for_logs', 0),
'important_logs': self.getint('Telegram', 'important_logs', 0),
'archive': self.getint('Telegram', 'archive', 0),
'test_group': self.getint('Telegram', 'test_group', 0)
}
# Settings секция
settings['Settings'] = {
'logs': self.getboolean('Settings', 'logs', False),
'test': self.getboolean('Settings', 'test', False)
}
return settings
# Global config instance
_config_instance: Optional[ConfigManager] = None
def get_config() -> ConfigManager:
"""Get global configuration instance."""
global _config_instance
if _config_instance is None:
_config_instance = ConfigManager()
return _config_instance