A Raycast extension to search and manage Semble Cards and Collections
at main 134 lines 3.9 kB view raw
1import { BskyAgent } from "@atproto/api"; 2import { getPreferenceValues } from "@raycast/api"; 3import { SembleCard, SembleCollection, SembleCollectionLink, CardWithCollection } from "./types"; 4 5interface Preferences { 6 identifier: string; 7 password: string; 8 pdsHost?: string; 9} 10 11export class SembleClient { 12 private agent: BskyAgent; 13 private preferences: Preferences; 14 private userDid: string | null = null; 15 16 constructor() { 17 this.preferences = getPreferenceValues<Preferences>(); 18 const pdsHost = this.preferences.pdsHost || "bsky.social"; 19 this.agent = new BskyAgent({ 20 service: `https://${pdsHost}`, 21 }); 22 } 23 24 async authenticate(): Promise<void> { 25 try { 26 const response = await this.agent.login({ 27 identifier: this.preferences.identifier, 28 password: this.preferences.password, 29 }); 30 this.userDid = response.data.did; 31 } catch (error) { 32 throw new Error(`Authentication failed: ${error instanceof Error ? error.message : "Unknown error"}`); 33 } 34 } 35 36 async getCards(): Promise<SembleCard[]> { 37 if (!this.userDid) { 38 await this.authenticate(); 39 } 40 41 try { 42 const response = await this.agent.com.atproto.repo.listRecords({ 43 repo: this.userDid!, 44 collection: "network.cosmik.card", 45 limit: 100, 46 }); 47 48 return response.data.records as unknown as SembleCard[]; 49 } catch (error) { 50 throw new Error(`Failed to fetch cards: ${error instanceof Error ? error.message : "Unknown error"}`); 51 } 52 } 53 54 async getCollections(): Promise<SembleCollection[]> { 55 if (!this.userDid) { 56 await this.authenticate(); 57 } 58 59 try { 60 const response = await this.agent.com.atproto.repo.listRecords({ 61 repo: this.userDid!, 62 collection: "network.cosmik.collection", 63 limit: 100, 64 }); 65 66 return response.data.records as unknown as SembleCollection[]; 67 } catch (error) { 68 throw new Error(`Failed to fetch collections: ${error instanceof Error ? error.message : "Unknown error"}`); 69 } 70 } 71 72 async getCollectionLinks(): Promise<SembleCollectionLink[]> { 73 if (!this.userDid) { 74 await this.authenticate(); 75 } 76 77 try { 78 const response = await this.agent.com.atproto.repo.listRecords({ 79 repo: this.userDid!, 80 collection: "network.cosmik.collectionLink", 81 limit: 100, 82 }); 83 84 return response.data.records as unknown as SembleCollectionLink[]; 85 } catch (error) { 86 throw new Error( 87 `Failed to fetch collection links: ${error instanceof Error ? error.message : "Unknown error"}` 88 ); 89 } 90 } 91 92 async getCardsWithCollections(): Promise<CardWithCollection[]> { 93 const [cards, collections, collectionLinks] = await Promise.all([ 94 this.getCards(), 95 this.getCollections(), 96 this.getCollectionLinks(), 97 ]); 98 99 // Create a map of collection URIs to collection objects 100 const collectionsMap = new Map<string, SembleCollection>(); 101 collections.forEach((col) => { 102 collectionsMap.set(col.uri, col); 103 }); 104 105 // Create a map of card URIs to their collections 106 const cardCollectionsMap = new Map<string, SembleCollection[]>(); 107 collectionLinks.forEach((link) => { 108 const cardUri = link.value.card.uri; 109 const collectionUri = link.value.collection.uri; 110 const collection = collectionsMap.get(collectionUri); 111 112 if (collection) { 113 if (!cardCollectionsMap.has(cardUri)) { 114 cardCollectionsMap.set(cardUri, []); 115 } 116 cardCollectionsMap.get(cardUri)!.push(collection); 117 } 118 }); 119 120 // Combine cards with their collections 121 return cards.map((card) => ({ 122 card, 123 collections: cardCollectionsMap.get(card.uri) || [], 124 })); 125 } 126 127 getUserIdentifier(): string { 128 return this.preferences.identifier; 129 } 130 131 getUserDid(): string | null { 132 return this.userDid; 133 } 134}