Atproto AMA app
1/**
2 * AT Protocol client wrapper for publishing Askimut records
3 */
4
5import { Client } from '@atproto/lex'
6import type { OAuthSession } from '@atproto/oauth-client-node'
7import { getOAuthClient } from './oauth'
8import { getCachedConfig, isPublishingEnabled } from './config'
9import * as lexicons from '../lexicons/index.js'
10import type { LexQuestion, LexAnswer, LexProfile } from './schema-bridge'
11
12/**
13 * AT Protocol client for Askimut operations
14 */
15export class AskimutAtClient {
16 private client: Client | null = null
17 private session: OAuthSession | null = null
18
19 constructor(session?: OAuthSession) {
20 if (session) {
21 this.session = session
22 this.client = new Client(session, {
23 validateRequest: getCachedConfig().atProtocol.strictValidation,
24 validateResponse: true,
25 strictResponseProcessing: getCachedConfig().atProtocol.strictValidation
26 })
27 }
28 }
29
30 /**
31 * Initialize client with OAuth session for a given DID
32 */
33 static async fromDid(did: string): Promise<AskimutAtClient> {
34 try {
35 const oauthClient = await getOAuthClient()
36 const session = await oauthClient.restore(did)
37 return new AskimutAtClient(session)
38 } catch (error) {
39 console.error('Failed to restore AT Protocol session:', error)
40 return new AskimutAtClient()
41 }
42 }
43
44 /**
45 * Check if the client is authenticated
46 */
47 isAuthenticated(): boolean {
48 return this.client !== null && this.session !== null
49 }
50
51 /**
52 * Get the authenticated user's DID
53 */
54 getDid(): string | null {
55 return this.session?.did || null
56 }
57
58 /**
59 * Publish a question to AT Protocol
60 */
61 async publishQuestion(question: LexQuestion): Promise<{ uri: string; cid: string } | null> {
62 if (!this.client || !isPublishingEnabled('question')) {
63 return null
64 }
65
66 try {
67 // Validate the question before publishing
68 lexicons.question.$validate(question)
69
70 // For now, we'll use a simplified approach until the Client API is more stable
71 console.log('Would publish question to AT Protocol:', question)
72
73 // Return a mock result for now
74 return {
75 uri: `at://${this.getDid()}/com.askimut.question/${Date.now()}`,
76 cid: 'bafyrei' + Math.random().toString(36).substring(2)
77 }
78 } catch (error) {
79 console.error('Failed to publish question to AT Protocol:', error)
80 throw error
81 }
82 }
83
84 /**
85 * Publish an answer to AT Protocol
86 */
87 async publishAnswer(answer: LexAnswer): Promise<{ uri: string; cid: string } | null> {
88 if (!this.client || !isPublishingEnabled('answer')) {
89 return null
90 }
91
92 try {
93 // Validate the answer before publishing
94 lexicons.answer.$validate(answer)
95
96 // For now, we'll use a simplified approach until the Client API is more stable
97 console.log('Would publish answer to AT Protocol:', answer)
98
99 // Return a mock result for now
100 return {
101 uri: `at://${this.getDid()}/com.askimut.answer/${Date.now()}`,
102 cid: 'bafyrei' + Math.random().toString(36).substring(2)
103 }
104 } catch (error) {
105 console.error('Failed to publish answer to AT Protocol:', error)
106 throw error
107 }
108 }
109
110 /**
111 * Publish/update a profile to AT Protocol
112 */
113 async publishProfile(profile: LexProfile): Promise<{ uri: string; cid: string } | null> {
114 if (!this.client || !isPublishingEnabled('profile')) {
115 return null
116 }
117
118 try {
119 // Validate the profile before publishing
120 lexicons.profile.$validate(profile)
121
122 // For now, we'll use a simplified approach until the Client API is more stable
123 console.log('Would publish profile to AT Protocol:', profile)
124
125 // Return a mock result for now
126 return {
127 uri: `at://${this.getDid()}/com.askimut.profile/self`,
128 cid: 'bafyrei' + Math.random().toString(36).substring(2)
129 }
130 } catch (error) {
131 console.error('Failed to publish profile to AT Protocol:', error)
132 throw error
133 }
134 }
135
136 // Note: Get, list, and delete methods are temporarily disabled
137 // until the Client API type issues are resolved in a future update
138}
139
140/**
141 * Create an AT Protocol client for a given session
142 */
143export async function createAtClient(session?: OAuthSession): Promise<AskimutAtClient> {
144 return new AskimutAtClient(session)
145}
146
147/**
148 * Create an AT Protocol client for a given DID
149 */
150export async function createAtClientForDid(did: string): Promise<AskimutAtClient> {
151 return AskimutAtClient.fromDid(did)
152}
153
154/**
155 * Utility function to extract record key (rkey) from AT-URI
156 */
157export function extractRkeyFromUri(atUri: string): string | null {
158 try {
159 const parts = atUri.split('/')
160 return parts[parts.length - 1] || null
161 } catch {
162 return null
163 }
164}
165
166/**
167 * Utility function to construct AT-URI from DID and rkey
168 */
169export function constructAtUri(did: string, collection: string, rkey: string): string {
170 return `at://${did}/${collection}/${rkey}`
171}