код не правильно работает. ТЗ: у меня есть 80 штук логинов, паролей и token. Token действителен 15 дней, если 15 прошло, то нужно его получить заново через login(login, password) вернет токен. Каждый токен можно использовать только 5 раз в сутки, после суток можно использовать повторно токен, не обновляя его (должна быть настройка с помощью которой можно указать utc когда будет сбрасываться). Нужно реализовать отдельный асинхронный класс который это все будет делать с сохранением в постоянную память. Получения токенов должно происходить по мере необходимости. В ДАННЫЙ МОМЕНТ НЕ ПРОИСХОДИТ ЗАМЕНА ТОКЕНА ЕСЛИ ОН УЖЕ БОЛЕЕ 5 РАЗ БЫЛ ИСПОЛЬЗОВАН #token.py import asyncio
from datetime import datetime, timedelta, timezone
import uuid
import aiohttp
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select
from .database.models import User, Credential, UserToken

class FeloTokenManager:
    def __init__(self, session: AsyncSession, utc_offset: int):
        self.session = session
        self.utc_offset = utc_offset

    async def login_api(self, email, password):
        url = 'https://account.felo.ai/api/user/sign.in'
        headers = {
            'Content-Type': 'application/json',
            'Accept': 'application/json, text/plain, */*',
            'Origin': 'https://account.felo.ai',
            'Referer': 'https://account.felo.ai/register',
        }

        device_id = uuid.uuid4().hex

        payload = {
            "email": email,
            "password": password,
            "app_id": "glaritySearch",
            "client_type": "WEB",
            "device_id": device_id
        }

        async with aiohttp.ClientSession() as session:
            async with session.post(url, headers=headers, json=payload) as response:
                if response.status == 200:
                    data = await response.json()
                    try:
                        token_value = data["data"]["token"]["token_value"]
                        return token_value
                    except KeyError:
                        raise Exception("Token value not found in the response data")
                else:
                    raise Exception(f"Request failed with status {response.status}")

    async def get_token_by_telegram_id(self, telegram_id):
        # Получаем пользователя
        result = await self.session.execute(select(User).where(User.telegram_id == telegram_id))
        user = result.scalars().first()
        if not user:
            raise Exception(f"User with telegram_id {telegram_id} not found.")

        # Получаем UserToken
        result = await self.session.execute(select(UserToken).where(UserToken.user_id == user.id))
        user_token = result.scalars().first()

        if not user_token:
            # Если у пользователя нет связанного Credential, находим доступный Credential
            credential = await self._get_available_credential()
            if not credential:
                raise Exception("No available credentials found.")
            # Связываем Credential с пользователем
            user_token = UserToken(user_id=user.id, credential_id=credential.id)
            self.session.add(user_token)
            await self.session.commit()
        else:
            credential = user_token.credential

        now = datetime.utcnow().replace(tzinfo=timezone.utc)
        adjusted_now = now + timedelta(hours=self.utc_offset)
        user_midnight = datetime.combine(adjusted_now.date(), datetime.min.time(), tzinfo=timezone.utc)
        reset_time = user_midnight - timedelta(hours=self.utc_offset)

        # Убедимся, что credential.expires_at и credential.reset_date являются aware datetime
        if credential.expires_at is not None and credential.expires_at.tzinfo is None:
            credential.expires_at = credential.expires_at.replace(tzinfo=timezone.utc)

        if credential.reset_date is not None and credential.reset_date.tzinfo is None:
            credential.reset_date = credential.reset_date.replace(tzinfo=timezone.utc)

        token_expired = credential.expires_at is None or credential.expires_at < now
        token_empty = credential.token is None or credential.token == ''

        # Проверяем, нужно ли сбросить usage_count
        if credential.reset_date is None or credential.reset_date < reset_time:
            credential.usage_count = 0
            credential.reset_date = reset_time

        if token_empty or token_expired or credential.usage_count >= 5:
            # Генерируем новый токен
            token_value = await self.login_api(credential.login, credential.password)
            credential.token = token_value
            credential.expires_at = now + timedelta(days=15)
            credential.usage_count = 0
            credential.reset_date = reset_time

        # Увеличиваем счётчик использования
        credential.usage_count += 1

        await self.session.commit()

        return credential.token

    async def _get_available_credential(self):
        # Ищем все Credentials
        result = await self.session.execute(select(Credential))
        credentials = result.scalars().all()

        now = datetime.utcnow().replace(tzinfo=timezone.utc)
        adjusted_now = now + timedelta(hours=self.utc_offset)
        user_midnight = datetime.combine(adjusted_now.date(), datetime.min.time(), tzinfo=timezone.utc)
        reset_time = user_midnight - timedelta(hours=self.utc_offset)

        for credential in credentials:
            # Убедимся, что даты являются timezone-aware
            if credential.expires_at is not None and credential.expires_at.tzinfo is None:
                credential.expires_at = credential.expires_at.replace(tzinfo=timezone.utc)
            if credential.reset_date is not None and credential.reset_date.tzinfo is None:
                credential.reset_date = credential.reset_date.replace(tzinfo=timezone.utc)

            # Проверяем, нужно ли сбросить usage_count
            if credential.reset_date is None or credential.reset_date < reset_time:
                credential.usage_count = 0
                credential.reset_date = reset_time

            token_expired = credential.expires_at is None or credential.expires_at < now
            token_empty = credential.token is None or credential.token == ''

            if token_empty or token_expired or credential.usage_count >= 5:
                # Генерируем новый токен
                token_value = await self.login_api(credential.login, credential.password)
                credential.token = token_value
                credential.expires_at = now + timedelta(days=15)
                credential.usage_count = 0
                credential.reset_date = reset_time

            # Увеличиваем счётчик использования
            credential.usage_count += 1

            await self.session.commit()

            # Возвращаем первый подходящий Credential
            return credential

        # Если нет доступных Credentials
        return None

    async def add_credential(self, login, password):
        # Проверяем, существует ли Credential
        result = await self.session.execute(select(Credential).where(Credential.login == login))
        existing_credential = result.scalars().first()
        if existing_credential:
            raise Exception("Credential with this login already exists.")

        new_credential = Credential(
            login=login,
            password=password,
            usage_count=0,
            reset_date=None,
            expires_at=None,
            token=None,
        )
        self.session.add(new_credential)
        await self.session.commit()
        return new_credential

    async def assign_credential_to_user(self, telegram_id, credential_login):
        # Получаем пользователя
        result = await self.session.execute(select(User).where(User.telegram_id == telegram_id))
        user = result.scalars().first()
        if not user:
            raise Exception(f"User with telegram_id {telegram_id} not found.")

        # Получаем Credential
        result = await self.session.execute(select(Credential).where(Credential.login == credential_login))
        credential = result.scalars().first()
        if not credential:
            raise Exception(f"Credential with login {credential_login} not found.")

        # Проверяем, существует ли UserToken
        result = await self.session.execute(select(UserToken).where(UserToken.user_id == user.id))
        user_token = result.scalars().first()
        if user_token:
            # Обновляем Credential, если отличается
            if user_token.credential_id != credential.id:
                user_token.credential_id = credential.id
                await self.session.commit()
            else:
                raise Exception("This credential is already assigned to the user.")
        else:
            # Создаём новый UserToken
            new_user_token = UserToken(
                user_id=user.id,
                credential_id=credential.id,
            )
            self.session.add(new_user_token)
            await self.session.commit()
            return new_user_token #models.py
 class User(Base):
    __tablename__ = 'sdxl_users'
    id = Column(Integer, primary_key=True, index=True)
    firstname = Column(String, nullable=True)
    lastname = Column(String, nullable=True)
    username = Column(String, nullable=True)
    telegram_id = Column(BigInteger, nullable=False, unique=True)
    date_joined = Column(DateTime, default=datetime.utcnow, nullable=False)
    utc_offset = Column(Integer, nullable=True)  # UTC смещение в часах

    prompts = relationship("Prompt", back_populates="user")
    reminders = relationship("Reminder", back_populates="user")
    user_tokens = relationship("UserToken", back_populates="user")
class Credential(Base):
    __tablename__ = 'sdxl_felo_credentials'
    id = Column(Integer, primary_key=True, index=True)
    login = Column(String, unique=True, nullable=False)
    password = Column(String, nullable=False)  # Хранить в открытом виде
    token = Column(String, unique=True, nullable=True)
    expires_at = Column(DateTime(timezone=True), nullable=True)
    reset_date = Column(DateTime(timezone=True), nullable=True)  # Дата последнего сброса
    usage_count = Column(Integer, default=0, nullable=False)

    user_tokens = relationship("UserToken", back_populates="credential",lazy="selectin")
class UserToken(Base):
    __tablename__ = 'sdxl_felo_user_tokens'
    id = Column(Integer, primary_key=True, index=True)
    user_id = Column(Integer, ForeignKey('sdxl_users.id', ondelete='CASCADE'), nullable=False)
    credential_id = Column(Integer, ForeignKey('sdxl_felo_credentials.id', ondelete='CASCADE'), nullable=False)

    user = relationship("User", back_populates="user_tokens",lazy="selectin")
    credential = relationship("Credential", back_populates="user_tokens", lazy="selectin")

    __table_args__ = (UniqueConstraint('user_id', 'credential_id', name='_user_token_uc'),)