- 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
206 lines
7.6 KiB
Python
206 lines
7.6 KiB
Python
"""
|
||
Управление пользователями и их учетными данными МойСклад
|
||
"""
|
||
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)
|