BUH_bot/modules/user_manager.py
Игорь Надеин 5b6ab02e53 Initial commit: Core bot application
- Add main bot file with Telegram handlers
- Add configuration module
- Add Python dependencies
- Add modules: excel_generator, moysklad_client, xml_generator, user_manager
- Add .gitignore for Python, Docker, user data
2025-11-03 14:19:48 +03:00

206 lines
7.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Управление пользователями и их учетными данными МойСклад
"""
import json
import logging
import os
from pathlib import Path
from typing import Dict, Optional
from cryptography.fernet import Fernet
logger = logging.getLogger(__name__)
class UserManager:
"""Управление пользователями и их учетными данными"""
def __init__(self, storage_file: str = None):
"""
Инициализация менеджера пользователей
Args:
storage_file: Путь к файлу хранения данных
"""
# Поддержка Docker volumes
data_dir = os.getenv('DATA_DIR', '.')
if storage_file is None:
storage_file = os.path.join(data_dir, "users.json")
self.storage_path = Path(storage_file)
self.key_file = Path(data_dir) / ".encryption_key"
self.users: Dict[int, Dict] = {}
# Создаём data директорию если не существует
self.storage_path.parent.mkdir(parents=True, exist_ok=True)
self._load_users()
# Генерация ключа шифрования
self.cipher_key = self._get_or_create_key()
self.cipher = Fernet(self.cipher_key)
def _get_or_create_key(self) -> bytes:
"""Получает или создаёт ключ шифрования"""
if self.key_file.exists():
with open(self.key_file, "rb") as f:
return f.read()
else:
key = Fernet.generate_key()
with open(self.key_file, "wb") as f:
f.write(key)
logger.info(f"Создан новый ключ шифрования: {self.key_file}")
return key
def _encrypt(self, text: str) -> str:
"""Шифрует текст"""
return self.cipher.encrypt(text.encode()).decode()
def _decrypt(self, encrypted_text: str) -> str:
"""Расшифровывает текст"""
return self.cipher.decrypt(encrypted_text.encode()).decode()
def _load_users(self):
"""Загружает пользователей из файла"""
if self.storage_path.exists():
try:
with open(self.storage_path, 'r', encoding='utf-8') as f:
data = json.load(f)
# Конвертируем ключи обратно в int
self.users = {int(k): v for k, v in data.items()}
logger.info(f"Загружено пользователей: {len(self.users)}")
except Exception as e:
logger.error(f"Ошибка загрузки пользователей: {e}")
self.users = {}
else:
logger.info("Файл пользователей не найден, создан новый")
self.users = {}
def _save_users(self):
"""Сохраняет пользователей в файл"""
try:
with open(self.storage_path, 'w', encoding='utf-8') as f:
json.dump(self.users, f, ensure_ascii=False, indent=2)
logger.info(f"Сохранено пользователей: {len(self.users)}")
except Exception as e:
logger.error(f"Ошибка сохранения пользователей: {e}")
def is_user_registered(self, user_id: int) -> bool:
"""
Проверяет, зарегистрирован ли пользователь
Args:
user_id: ID пользователя Telegram
Returns:
True если пользователь зарегистрирован
"""
return user_id in self.users
def register_user(
self,
user_id: int,
moysklad_login: str,
moysklad_password: str,
username: Optional[str] = None
) -> bool:
"""
Регистрирует нового пользователя
Args:
user_id: ID пользователя Telegram
moysklad_login: Логин МойСклад
moysklad_password: Пароль МойСклад
username: Username Telegram (опционально)
Returns:
True если успешно зарегистрирован
"""
try:
self.users[user_id] = {
"username": username,
"moysklad_login": self._encrypt(moysklad_login),
"moysklad_password": self._encrypt(moysklad_password),
"registered_at": None # Можно добавить timestamp
}
self._save_users()
logger.info(f"Пользователь {user_id} зарегистрирован")
return True
except Exception as e:
logger.error(f"Ошибка регистрации пользователя {user_id}: {e}")
return False
def get_user_credentials(self, user_id: int) -> Optional[Dict[str, str]]:
"""
Получает учетные данные пользователя
Args:
user_id: ID пользователя Telegram
Returns:
Dict с login и password или None
"""
if user_id not in self.users:
return None
try:
user_data = self.users[user_id]
return {
"login": self._decrypt(user_data["moysklad_login"]),
"password": self._decrypt(user_data["moysklad_password"])
}
except Exception as e:
logger.error(f"Ошибка получения credentials для {user_id}: {e}")
return None
def update_credentials(
self,
user_id: int,
moysklad_login: str,
moysklad_password: str
) -> bool:
"""
Обновляет учетные данные пользователя
Args:
user_id: ID пользователя Telegram
moysklad_login: Новый логин МойСклад
moysklad_password: Новый пароль МойСклад
Returns:
True если успешно обновлено
"""
if user_id not in self.users:
return False
try:
self.users[user_id]["moysklad_login"] = self._encrypt(moysklad_login)
self.users[user_id]["moysklad_password"] = self._encrypt(moysklad_password)
self._save_users()
logger.info(f"Credentials обновлены для пользователя {user_id}")
return True
except Exception as e:
logger.error(f"Ошибка обновления credentials для {user_id}: {e}")
return False
def delete_user(self, user_id: int) -> bool:
"""
Удаляет пользователя
Args:
user_id: ID пользователя Telegram
Returns:
True если успешно удален
"""
if user_id in self.users:
del self.users[user_id]
self._save_users()
logger.info(f"Пользователь {user_id} удален")
return True
return False
def get_user_count(self) -> int:
"""Возвращает количество зарегистрированных пользователей"""
return len(self.users)