data endpoint for entity 90008 (aka. a website)
1import { env } from '$env/dynamic/private';
2import { AppBskyFeedPost } from '@atcute/bluesky';
3
4import { Client, CredentialManager, ok, simpleFetchHandler } from '@atcute/client';
5import { parse, type CanonicalResourceUri, type Did } from '@atcute/lexicons';
6import { get, writable } from 'svelte/store';
7
8export const PDS_URL = 'https://gaze.systems';
9export const IDENTIFIER = 'did:web:guestbook.gaze.systems';
10
11const constellationClient = new Client({
12 handler: simpleFetchHandler({ service: 'https://constellation.microcosm.blue' })
13});
14const userClient = new Client({
15 handler: simpleFetchHandler({ service: 'https://zwsp.xyz' })
16});
17const guestbookClient = writable<null | Client>(null);
18
19export type Post = {
20 record: AppBskyFeedPost.Main;
21 uri: CanonicalResourceUri;
22};
23
24export const getGuestbookClient = async () => {
25 try {
26 let client = get(guestbookClient);
27 if (client === null) {
28 client = await loginToBsky();
29 guestbookClient.set(client);
30 }
31 return client;
32 } catch (e) {
33 throw `cant login to bsky: ${e}`;
34 }
35};
36
37const loginToBsky = async () => {
38 const password = env.BSKY_PASSWORD ?? null;
39 if (password === null) {
40 throw new Error('no password provided');
41 }
42 const handler = new CredentialManager({ service: PDS_URL });
43 const rpc = new Client({ handler });
44 await handler.login({ identifier: IDENTIFIER, password });
45 return rpc;
46};
47
48export const getUserPosts = async (
49 client: Client,
50 repo: Did,
51 count: number = 10,
52 cursor?: string
53) => {
54 const posts: Post[] = [];
55 // fetch requested amount of posts
56 while (posts.length < count - 1) {
57 const fetched = ok(
58 await client.get('com.atproto.repo.listRecords', {
59 params: { repo, collection: 'app.bsky.feed.post', cursor, limit: count }
60 })
61 );
62 for (const record of fetched.records) {
63 const post = parse(AppBskyFeedPost.mainSchema, record.value);
64 if (post.reply) continue;
65 posts.push({
66 record: post,
67 uri: record.uri as CanonicalResourceUri
68 });
69 }
70 cursor = fetched.cursor;
71 if (cursor === undefined) {
72 break;
73 }
74 }
75 return { posts: posts.slice(0, count), cursor };
76};
77
78const lastPosts = writable<Post[]>([]);
79
80export const updateLastPosts = async () => {
81 try {
82 const { posts } = await getUserPosts(userClient, 'did:plc:dfl62fgb7wtjj3fcbb72naae', 10);
83 lastPosts.set(posts);
84 } catch (err) {
85 console.log(`can't update last posts ${err}`);
86 }
87};
88
89export const getLastPosts = () => {
90 return get(lastPosts);
91};
92
93export const getReplies = async (client: Client, postUri: CanonicalResourceUri, forDid?: Did) => {
94 // todo: do cursor stuff here later if it matters
95 const backlinks = ok(
96 await constellationClient.get('blue.microcosm.links.getBacklinks', {
97 params: {
98 did: forDid ? [forDid] : [],
99 subject: postUri,
100 source: 'app.bsky.feed.post:reply.parent.uri'
101 }
102 })
103 );
104 const replies: Post[] = [];
105 for (const record of backlinks.records) {
106 const fetched = ok(
107 await client.get('com.atproto.repo.getRecord', {
108 params: { repo: record.did, collection: record.collection, rkey: record.rkey }
109 })
110 );
111 const post = parse(AppBskyFeedPost.mainSchema, fetched.value);
112 replies.push({ record: post, uri: fetched.uri as CanonicalResourceUri });
113 }
114 return replies;
115};