"""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