from abc import ABC, abstractmethod from typing import Any, override from atproto.identity import did_resolver, handle_resolver from cross.service import Service from util.util import normalize_service_url SERVICE = "https://bsky.app" def validate_and_transform(data: dict[str, Any]): if not data["handle"] and not data["did"]: raise KeyError("no 'handle' or 'did' specified for bluesky input!") if "did" in data: did = str(data["did"]) # only did:web and did:plc are supported if not did.startswith("did:plc:") and not did.startswith("did:web:"): raise ValueError(f"Invalid handle {did}!") if "pds" in data: data["pds"] = normalize_service_url(data["pds"]) class BlueskyService(ABC, Service): pds: str did: str def _init_identity(self) -> None: handle, did, pds = self.get_identity_options() if did and pds: self.did = did self.pds = pds return if not did: if not handle: raise KeyError("No did: or atproto handle provided!") self.log.info("Resolving ATP identity for %s...", handle) self.did = handle_resolver.resolve_handle(handle) if not pds: self.log.info("Resolving PDS from %s DID document...", did) atp_pds = did_resolver.resolve_did(self.did).get_atproto_pds() if not atp_pds: raise Exception("Failed to resolve atproto pds for %s") self.pds = atp_pds @abstractmethod def get_identity_options(self) -> tuple[str | None, str | None, str | None]: pass