"""Card synchronization service for atproto""" from typing import List, Dict, Any, Optional from datetime import datetime from sqlalchemy.ext.asyncio import AsyncSession # from app.services.atproto import AtprotoService, CardLexicon from app.repositories.card import CardRepository from app.repositories.user import UserRepository from app.models.card import Card as CardModel from app.db.models import UserCard from app.core.config import settings class CardSyncService: """Service for syncing cards between database and atproto PDS""" def __init__(self, session: AsyncSession, atproto_session: Optional[str] = None): self.db_session = session self.card_repo = CardRepository(session) self.user_repo = UserRepository(session) self.atproto_service = AtprotoService() # Restore atproto session if provided if atproto_session: self.atproto_service.restore_session(atproto_session) async def sync_card_to_pds(self, user_card: UserCard, user_did: str) -> Optional[str]: """ Sync a single card to user's PDS Args: user_card: Card from database user_did: User's DID Returns: Record URI if successful """ if not settings.atproto_handle or not settings.atproto_password: # Skip if atproto credentials not configured return None try: # Login if not already if not self.atproto_service.client: await self.atproto_service.login( settings.atproto_handle, settings.atproto_password ) # Convert to API model card_model = CardModel( id=user_card.card_id, cp=user_card.cp, status=user_card.status, skill=user_card.skill, owner_did=user_did, obtained_at=user_card.obtained_at, is_unique=user_card.is_unique, unique_id=str(user_card.unique_id) if user_card.unique_id else None ) # Create record in PDS uri = await self.atproto_service.create_card_record( did=user_did, card=card_model, collection=CardLexicon.LEXICON_ID ) # Store URI in database for future reference # (You might want to add a field to UserCard model for this) return uri except Exception as e: print(f"Failed to sync card to PDS: {e}") return None async def sync_all_user_cards(self, user_id: int, user_did: str) -> int: """ Sync all user's cards to PDS Args: user_id: Database user ID user_did: User's DID Returns: Number of cards synced """ # Get all user cards from database user_cards = await self.card_repo.get_user_cards(user_id) synced_count = 0 for card in user_cards: uri = await self.sync_card_to_pds(card, user_did) if uri: synced_count += 1 return synced_count async def import_cards_from_pds(self, user_did: str) -> int: """ Import cards from user's PDS to database Args: user_did: User's DID Returns: Number of cards imported """ if not self.atproto_service.client: return 0 # Get user from database user = await self.user_repo.get_by_did(user_did) if not user: return 0 # Get cards from PDS pds_cards = await self.atproto_service.get_user_cards( did=user_did, collection=CardLexicon.LEXICON_ID ) imported_count = 0 for pds_card in pds_cards: # Check if card already exists existing_count = await self.card_repo.count_user_cards( user.id, pds_card.get("cardId") ) if existing_count == 0: # Import card await self.card_repo.create_user_card( user_id=user.id, card_id=pds_card.get("cardId"), cp=pds_card.get("cp"), status=pds_card.get("status"), skill=pds_card.get("skill"), is_unique=pds_card.get("isUnique", False) ) imported_count += 1 await self.db_session.commit() return imported_count async def verify_card_ownership( self, user_did: str, card_id: int, unique_id: Optional[str] = None ) -> bool: """ Verify user owns a card by checking both database and PDS Args: user_did: User's DID card_id: Card type ID unique_id: Unique ID for unique cards Returns: True if user owns the card """ # Check database first user = await self.user_repo.get_by_did(user_did) if user: user_cards = await self.card_repo.get_user_cards(user.id) for card in user_cards: if card.card_id == card_id: if not unique_id or str(card.unique_id) == unique_id: return True # Check PDS if configured if self.atproto_service.client: try: pds_cards = await self.atproto_service.get_user_cards(user_did) for card in pds_cards: if card.get("cardId") == card_id: if not unique_id or card.get("uniqueId") == unique_id: return True except Exception: pass return False