aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillermo Serrahima <dev@vylion.com>2026-02-19 14:27:24 +0100
committerGuillermo Serrahima <dev@vylion.com>2026-02-19 14:27:24 +0100
commit8f39d3c036588782f36d5ff90ce2839fd75fa496 (patch)
tree38bfd8304057828b4e30d704b093e79f6b931bb3
parentb8614b16e958cbacfff05a8e2f79f8e4410654b8 (diff)
downloadllostre-8f39d3c036588782f36d5ff90ce2839fd75fa496.tar.xz
llostre-8f39d3c036588782f36d5ff90ce2839fd75fa496.zip
added bsky posts
-rw-r--r--backend/fetchers/app.bsky.go47
-rw-r--r--backend/notes/app.bsky.feed.post.go74
-rw-r--r--backend/notes/note.go13
3 files changed, 134 insertions, 0 deletions
diff --git a/backend/fetchers/app.bsky.go b/backend/fetchers/app.bsky.go
new file mode 100644
index 0000000..df89249
--- /dev/null
+++ b/backend/fetchers/app.bsky.go
@@ -0,0 +1,47 @@
+package fetchers
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "llostre/backend/notes"
+ "llostre/backend/records"
+ "net/http"
+)
+
+type ListRecordsResponse struct {
+ Records []notes.AppBskyFeedPostRecord `json:"records"`
+}
+
+func FetchBlueskyPosts(did string, pdsURL string) ([]notes.AppBskyFeedPostNote, error) {
+ url := fmt.Sprintf("%s/xrpc/com.atproto.repo.listRecords?repo=%s&collection=app.bsky.feed.post&limit=50", pdsURL, did)
+
+ resp, err := http.Get(url)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ var response records.ListRecordsResponse
+ err = json.Unmarshal(body, &response)
+ if err != nil {
+ return nil, err
+ }
+
+ var posts []notes.AppBskyFeedPostNote
+ for _, record := range response.Records {
+ bp, err := notes.AppBskyFeedPostFromRecord(record)
+ if err != nil {
+ fmt.Printf("skipping malformed record %s: %v\n", record.URI, err)
+ continue
+ }
+ posts = append(posts, *bp)
+ }
+
+ return posts, nil
+}
diff --git a/backend/notes/app.bsky.feed.post.go b/backend/notes/app.bsky.feed.post.go
new file mode 100644
index 0000000..21256be
--- /dev/null
+++ b/backend/notes/app.bsky.feed.post.go
@@ -0,0 +1,74 @@
+package notes
+
+import (
+ "fmt"
+ "strings"
+ "time"
+)
+
+type AppBskyFeedPostNote struct {
+ URI string
+ AuthorDID string
+ AuthorHandle string
+ Text string
+ CreatedAt time.Time
+ ReplyTo *string
+}
+
+type AppBskyFeedPostRecord struct {
+ URI string `json:"uri"`
+ Value struct {
+ Text string `json:"text"`
+ CreatedAt string `json:"createdAt"`
+ Reply *struct {
+ Parent struct {
+ URI string `json:"uri"`
+ } `json:"parent"`
+ } `json:"reply,omitempty"`
+ } `json:"value"`
+}
+
+func (bp *AppBskyFeedPostNote) GetID() string {
+ return bp.URI
+}
+
+func (bp *AppBskyFeedPostNote) GetAuthorDID() string {
+ return bp.AuthorDID
+}
+
+func (bp *AppBskyFeedPostNote) GetAuthorHandle() string {
+ return bp.AuthorHandle
+}
+
+func (bp *AppBskyFeedPostNote) GetTimestamp() time.Time {
+ return bp.CreatedAt
+}
+
+func (bp *AppBskyFeedPostNote) GetReplyTo() *string {
+ return bp.ReplyTo
+}
+
+func AppBskyFeedPostFromRecord(record AppBskyFeedPostRecord) (*AppBskyFeedPostNote, error) {
+ createdAt, err := time.Parse(time.RFC3339, record.Value.CreatedAt)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse timestamp: %w", err)
+ }
+ if record.Value.Text == "" {
+ return nil, fmt.Errorf("missing required field: text")
+ }
+ if record.URI == "" {
+ return nil, fmt.Errorf("missing required field: uri")
+ }
+
+ parts := strings.Split(record.URI, "/") // at://DID/collection/rkey
+ did := parts[2]
+
+ return &AppBskyFeedPostNote{
+ URI: record.URI,
+ AuthorDID: did,
+ AuthorHandle: "author_handle", // wip
+ CreatedAt: createdAt,
+ Text: record.Value.Text,
+ ReplyTo: &record.Value.Reply.Parent.URI,
+ }, nil
+}
diff --git a/backend/notes/note.go b/backend/notes/note.go
new file mode 100644
index 0000000..1d92948
--- /dev/null
+++ b/backend/notes/note.go
@@ -0,0 +1,13 @@
+package notes
+
+import (
+ "time"
+)
+
+type Note interface {
+ GetID() string
+ GetAuthorDID() string
+ GetAuthorHandle() string
+ GetTimestamp() time.Time
+ GetReplyTo() *string
+}