A community based topic aggregation platform built on atproto
1package main
2
3import (
4 "database/sql"
5 "fmt"
6 "log"
7 "math/rand"
8 "time"
9
10 _ "github.com/lib/pq"
11)
12
13const (
14 postURI = "at://did:plc:hcuo3qx2lr7h7dquusbeobht/social.coves.community.post/3m56mowhbuk22"
15 postCID = "bafyreibml4midgt7ojq7dnabnku5ikzro4erfvdux6mmiqeat7pci2gy4u"
16 communityDID = "did:plc:hcuo3qx2lr7h7dquusbeobht"
17)
18
19type User struct {
20 DID string
21 Handle string
22 Name string
23}
24
25type Comment struct {
26 URI string
27 CID string
28 RKey string
29 DID string
30 RootURI string
31 RootCID string
32 ParentURI string
33 ParentCID string
34 Content string
35 CreatedAt time.Time
36}
37
38// Escalating conversation between two users
39var deepThreadConversation = []string{
40 "Wait, I just realized - if they both get suspended for this, their fantasy managers are SCREWED 😂",
41 "Bro imagine being in a league where you have BOTH Duren brothers and they both get suspended for fighting EACH OTHER",
42 "That's actually hilarious. 'Dear commissioner, my players got suspended for fighting... with each other'",
43 "The fantasy implications are wild. Do you get negative points for your players fighting your other players? 🤔",
44 "New fantasy category: Family Feuds. Duren brothers leading the league in FFD (Family Fight Disqualifications)",
45 "I'm dying 💀 FFD should absolutely be a stat. The Morris twins would've been unstoppable in that category",
46 "Don't forget the Plumlees! Those boys used to scrap in college practices. FFD Hall of Famers",
47 "Okay but serious question: has there EVER been brothers fighting each other in an NBA game before this? This has to be a first",
48 "I've been watching the NBA for 30 years and I can't think of a single time. This might genuinely be historic family beef",
49 "So we're witnessing NBA history right now. Not the good kind, but history nonetheless. Their mom is SO proud 😂",
50}
51
52var userHandles = []string{
53 "deep_thread_guy_1.bsky.social",
54 "deep_thread_guy_2.bsky.social",
55}
56
57func generateTID() string {
58 now := time.Now().UnixMicro()
59 return fmt.Sprintf("%d%04d", now, rand.Intn(10000))
60}
61
62func createUser(db *sql.DB, handle string, idx int) (*User, error) {
63 did := fmt.Sprintf("did:plc:deepthread%d%d", time.Now().Unix(), idx)
64 user := &User{
65 DID: did,
66 Handle: handle,
67 Name: handle,
68 }
69
70 query := `
71 INSERT INTO users (did, handle, pds_url, created_at, updated_at)
72 VALUES ($1, $2, $3, NOW(), NOW())
73 ON CONFLICT (did) DO NOTHING
74 `
75
76 _, err := db.Exec(query, user.DID, user.Handle, "http://localhost:3001")
77 if err != nil {
78 return nil, fmt.Errorf("failed to create user: %w", err)
79 }
80
81 log.Printf("Created user: %s (%s)", user.Handle, user.DID)
82 return user, nil
83}
84
85func createComment(db *sql.DB, user *User, content, parentURI, parentCID string, createdAt time.Time) (*Comment, error) {
86 rkey := generateTID()
87 uri := fmt.Sprintf("at://%s/social.coves.community.comment/%s", user.DID, rkey)
88 cid := fmt.Sprintf("bafy%s", rkey)
89
90 comment := &Comment{
91 URI: uri,
92 CID: cid,
93 RKey: rkey,
94 DID: user.DID,
95 RootURI: postURI,
96 RootCID: postCID,
97 ParentURI: parentURI,
98 ParentCID: parentCID,
99 Content: content,
100 CreatedAt: createdAt,
101 }
102
103 query := `
104 INSERT INTO comments (
105 uri, cid, rkey, commenter_did, root_uri, root_cid,
106 parent_uri, parent_cid, content, created_at, indexed_at
107 ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, NOW())
108 ON CONFLICT (uri) DO NOTHING
109 RETURNING id
110 `
111
112 var id int64
113 err := db.QueryRow(query,
114 comment.URI, comment.CID, comment.RKey, comment.DID,
115 comment.RootURI, comment.RootCID, comment.ParentURI, comment.ParentCID,
116 comment.Content, comment.CreatedAt,
117 ).Scan(&id)
118 if err != nil {
119 return nil, fmt.Errorf("failed to create comment: %w", err)
120 }
121
122 log.Printf("Level %d: %s", getCurrentLevel(parentURI), content)
123 return comment, nil
124}
125
126func getCurrentLevel(parentURI string) int {
127 if parentURI == postURI {
128 return 1
129 }
130 // Count how many times we've nested (rough estimate)
131 return 2 // Will be incremented as we go
132}
133
134func updateCommentCount(db *sql.DB, parentURI string, isPost bool) error {
135 if isPost {
136 _, err := db.Exec(`
137 UPDATE posts
138 SET comment_count = comment_count + 1
139 WHERE uri = $1
140 `, parentURI)
141 return err
142 }
143
144 _, err := db.Exec(`
145 UPDATE comments
146 SET reply_count = reply_count + 1
147 WHERE uri = $1
148 `, parentURI)
149 return err
150}
151
152func main() {
153 dbURL := "postgres://dev_user:dev_password@localhost:5435/coves_dev?sslmode=disable"
154 db, err := sql.Open("postgres", dbURL)
155 if err != nil {
156 log.Fatalf("Failed to connect to database: %v", err)
157 }
158 defer db.Close()
159
160 if err := db.Ping(); err != nil {
161 log.Fatalf("Failed to ping database: %v", err)
162 }
163
164 log.Println("Connected to database successfully!")
165 log.Println("Creating 10-level deep comment thread...")
166
167 rand.Seed(time.Now().UnixNano())
168
169 // Create two users who will have the back-and-forth
170 user1, err := createUser(db, userHandles[0], 1)
171 if err != nil {
172 log.Fatalf("Failed to create user 1: %v", err)
173 }
174
175 user2, err := createUser(db, userHandles[1], 2)
176 if err != nil {
177 log.Fatalf("Failed to create user 2: %v", err)
178 }
179
180 baseTime := time.Now().Add(-30 * time.Minute)
181
182 // Create the 10-level deep thread
183 parentURI := postURI
184 parentCID := postCID
185 isPost := true
186
187 for i, content := range deepThreadConversation {
188 // Alternate between users
189 user := user1
190 if i%2 == 1 {
191 user = user2
192 }
193
194 createdAt := baseTime.Add(time.Duration(i*2) * time.Minute)
195
196 comment, err := createComment(db, user, content, parentURI, parentCID, createdAt)
197 if err != nil {
198 log.Fatalf("Failed to create comment at level %d: %v", i+1, err)
199 }
200
201 // Update parent's reply count
202 if err := updateCommentCount(db, parentURI, isPost); err != nil {
203 log.Printf("Warning: Failed to update comment count: %v", err)
204 }
205
206 // Set this comment as the parent for the next iteration
207 parentURI = comment.URI
208 parentCID = comment.CID
209 isPost = false
210
211 time.Sleep(10 * time.Millisecond)
212 }
213
214 log.Println("\n=== Summary ===")
215 log.Printf("Created 10-level deep comment thread")
216 log.Printf("Thread participants: %s and %s", user1.Handle, user2.Handle)
217 log.Println("Done! Check the NBACentral post for the deep thread.")
218}