A community based topic aggregation platform built on atproto
at main 13 kB view raw
1package main 2 3import ( 4 "database/sql" 5 "fmt" 6 "log" 7 "math/rand" 8 "time" 9 10 _ "github.com/lib/pq" 11) 12 13// Post URI for "Your son don't wanna be here..." NBACentral post 14// at://did:plc:hcuo3qx2lr7h7dquusbeobht/social.coves.community.post/3m56mowhbuk22 15 16const ( 17 postURI = "at://did:plc:hcuo3qx2lr7h7dquusbeobht/social.coves.community.post/3m56mowhbuk22" 18 postCID = "bafyreibml4midgt7ojq7dnabnku5ikzro4erfvdux6mmiqeat7pci2gy4u" 19 communityDID = "did:plc:hcuo3qx2lr7h7dquusbeobht" 20) 21 22type User struct { 23 DID string 24 Handle string 25 Name string 26} 27 28type Comment struct { 29 URI string 30 CID string 31 RKey string 32 DID string 33 RootURI string 34 RootCID string 35 ParentURI string 36 ParentCID string 37 Content string 38 CreatedAt time.Time 39} 40 41var userNames = []string{ 42 "lakers_fan_23", "pistons_nation", "nba_historian", "hoops_enthusiast", 43 "detroit_pride", "basketball_iq", "courtside_view", "rim_protector", 44 "three_point_specialist", "paint_beast", "fast_break_fan", "clutch_time", 45 "triple_double_king", "defense_wins", "small_ball_era", "old_school_hoops", 46 "draft_expert", "salary_cap_guru", "trade_machine", "basketball_analytics", 47 "box_score_reader", "eye_test_guy", "film_room_analyst", "player_development", 48 "hometown_hero", "bandwagon_fan", "loyal_since_day_one", "casual_viewer", 49 "die_hard_supporter", "armchair_coach", "nbatv_addict", "league_pass_subscriber", 50} 51 52var topLevelComments = []string{ 53 "Imagine having to explain to your mom at Thanksgiving that you got ejected for fighting your brother 💀", 54 "Mrs. Duren watching this at home like 'I didn't raise y'all like this'", 55 "Their mom is somewhere absolutely LIVID right now. Both of them getting the belt when they get home", 56 "This is the most expensive sibling rivalry in history lmao", 57 "Jalen really said 'I've been whooping your ass since we were kids, what makes you think tonight's different' 😂", 58 "Ausar thought the NBA would protect him from his older brother. He thought wrong.", 59 "The trash talk must have been PERSONAL. That's years of sibling beef coming out", 60 "Family group chat is gonna be awkward after this one", 61 "Their parents spent 18 years breaking up fights just for it to happen on national TV", 62 "This is what happens when little bro thinks he's tough now that he's in the league", 63 "Jalen's been dunking on Ausar in the driveway for years, this was just another Tuesday for him", 64 "The fact that they're both in the league and THIS is how they settle it 💀💀💀", 65 "Ausar: 'I'm in the NBA now, I'm not scared of you anymore' - Jalen: 'BET'", 66 "Mom definitely called both of them after the game. Neither one answered lol", 67 "This is the content I pay League Pass for. Brothers getting into it on the court is peak entertainment", 68 "Thanksgiving dinner is about to be TENSE in the Duren/Thompson household", 69 "Little brother energy vs Big brother authority. Tale as old as time", 70 "The refs trying to break them up like 'Sir that's your BROTHER'", 71 "Jalen been waiting for this moment since Ausar got drafted", 72 "Both of them getting fined and their mom making them split the cost 😂", 73 "This brings me back to fighting my brother over the last piece of pizza. Just at a much higher tax bracket", 74 "The Pistons and Rockets staff trying to separate them: 'Guys we have practice tomorrow!'", 75 "Ausar finally tall enough to talk back and chose violence", 76 "Their dad watching like 'At least wait til you're both All-Stars before embarrassing the family'", 77 "This is what decades of 'Mom said it's my turn on the Xbox' leads to", 78} 79 80var replyComments = []string{ 81 "LMAOOO facts, mom's not playing", 82 "Bro I'm crying at this visual 😂😂😂", 83 "This is the one right here 💀", 84 "Thanksgiving about to be SILENT", 85 "You know their dad had flashbacks to breaking up driveway fights", 86 "The family group chat IS ON FIRE right now I guarantee it", 87 "Little bro syndrome is real and Ausar has it BAD", 88 "Big facts. Jalen been the big brother his whole life, NBA don't change that", 89 "Mom's gonna make them hug it out before Christmas I'm calling it now", 90 "This comment wins 😂😂😂", 91 "I need the full footage of what was said because it had to be PERSONAL", 92 "Years of sibling rivalry just exploded on NBA hardwood", 93 "The refs were so confused trying to separate family members 💀", 94 "Both of them getting the 'I'm disappointed' text from mom", 95 "Ausar thought NBA money meant he was safe. Nope.", 96 "Jalen's been waiting to humble him since draft night", 97 "This is exactly what their parents warned them about lmao", 98 "The fine money coming out of their allowance fr fr", 99 "Peak sibling behavior. I respect it.", 100 "Someone check on Mrs. Duren she's probably stress eating rn", 101} 102 103var deepReplyComments = []string{ 104 "And you KNOW mom's taking both their sides AND neither side at the same time", 105 "Family dynamics don't stop just cause you're making millions. Big brother gonna big brother", 106 "This thread has me in TEARS. Y'all are hilarious 😭", 107 "The fact that NBA refs had to break up a family dispute is sending me", 108 "Both of them are gonna act like nothing happened next family reunion", 109 "I guarantee their teammates are ROASTING them in the group chats right now", 110 "This is the most relatable NBA drama I've ever seen. We all fought our siblings", 111 "Mom's calling BOTH coaches after this I just know it", 112 "The league office trying to figure out how to fine siblings for fighting each other", 113 "This is gonna be an amazing 30 for 30 one day: 'What if I told you family and basketball don't always mix'", 114} 115 116func generateTID() string { 117 // Simple TID generator for testing (timestamp in microseconds + random) 118 now := time.Now().UnixMicro() 119 return fmt.Sprintf("%d%04d", now, rand.Intn(10000)) 120} 121 122func createUser(db *sql.DB, handle, name string, idx int) (*User, error) { 123 did := fmt.Sprintf("did:plc:testuser%d%d", time.Now().Unix(), idx) 124 user := &User{ 125 DID: did, 126 Handle: handle, 127 Name: name, 128 } 129 130 query := ` 131 INSERT INTO users (did, handle, pds_url, created_at, updated_at) 132 VALUES ($1, $2, $3, NOW(), NOW()) 133 ON CONFLICT (did) DO NOTHING 134 ` 135 136 _, err := db.Exec(query, user.DID, user.Handle, "http://localhost:3001") 137 if err != nil { 138 return nil, fmt.Errorf("failed to create user: %w", err) 139 } 140 141 log.Printf("Created user: %s (%s)", user.Handle, user.DID) 142 return user, nil 143} 144 145func createComment(db *sql.DB, user *User, content, parentURI, parentCID string, createdAt time.Time) (*Comment, error) { 146 rkey := generateTID() 147 uri := fmt.Sprintf("at://%s/social.coves.community.comment/%s", user.DID, rkey) 148 cid := fmt.Sprintf("bafy%s", rkey) 149 150 comment := &Comment{ 151 URI: uri, 152 CID: cid, 153 RKey: rkey, 154 DID: user.DID, 155 RootURI: postURI, 156 RootCID: postCID, 157 ParentURI: parentURI, 158 ParentCID: parentCID, 159 Content: content, 160 CreatedAt: createdAt, 161 } 162 163 query := ` 164 INSERT INTO comments ( 165 uri, cid, rkey, commenter_did, root_uri, root_cid, 166 parent_uri, parent_cid, content, created_at, indexed_at 167 ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, NOW()) 168 ON CONFLICT (uri) DO NOTHING 169 RETURNING id 170 ` 171 172 var id int64 173 err := db.QueryRow(query, 174 comment.URI, comment.CID, comment.RKey, comment.DID, 175 comment.RootURI, comment.RootCID, comment.ParentURI, comment.ParentCID, 176 comment.Content, comment.CreatedAt, 177 ).Scan(&id) 178 if err != nil { 179 return nil, fmt.Errorf("failed to create comment: %w", err) 180 } 181 182 log.Printf("Created comment by %s: %.50s...", user.Handle, content) 183 return comment, nil 184} 185 186func updateCommentCount(db *sql.DB, parentURI string, isPost bool) error { 187 if isPost { 188 _, err := db.Exec(` 189 UPDATE posts 190 SET comment_count = comment_count + 1 191 WHERE uri = $1 192 `, parentURI) 193 return err 194 } 195 196 _, err := db.Exec(` 197 UPDATE comments 198 SET reply_count = reply_count + 1 199 WHERE uri = $1 200 `, parentURI) 201 return err 202} 203 204func main() { 205 // Connect to dev database 206 dbURL := "postgres://dev_user:dev_password@localhost:5435/coves_dev?sslmode=disable" 207 db, err := sql.Open("postgres", dbURL) 208 if err != nil { 209 log.Fatalf("Failed to connect to database: %v", err) 210 } 211 defer db.Close() 212 213 if err := db.Ping(); err != nil { 214 log.Fatalf("Failed to ping database: %v", err) 215 } 216 217 log.Println("Connected to database successfully!") 218 log.Printf("Post URI: %s", postURI) 219 log.Println("Starting to generate NBA test comments...") 220 221 rand.Seed(time.Now().UnixNano()) 222 223 // Create users 224 log.Println("\n=== Creating Users ===") 225 users := make([]*User, 0, len(userNames)) 226 for i, name := range userNames { 227 handle := fmt.Sprintf("%s.bsky.social", name) 228 user, err := createUser(db, handle, name, i) 229 if err != nil { 230 log.Printf("Warning: Failed to create user %s: %v", name, err) 231 continue 232 } 233 users = append(users, user) 234 } 235 236 log.Printf("\nCreated %d users", len(users)) 237 238 // Generate comments with varied timing 239 log.Println("\n=== Creating Top-Level Comments ===") 240 baseTime := time.Now().Add(-3 * time.Hour) // Comments from 3 hours ago 241 topLevelCommentsCreated := make([]*Comment, 0) 242 243 // Create 18-22 top-level comments 244 numTopLevel := 18 + rand.Intn(5) 245 for i := 0; i < numTopLevel && i < len(users) && i < len(topLevelComments); i++ { 246 user := users[i] 247 content := topLevelComments[i] 248 createdAt := baseTime.Add(time.Duration(i*4+rand.Intn(3)) * time.Minute) 249 250 comment, err := createComment(db, user, content, postURI, postCID, createdAt) 251 if err != nil { 252 log.Printf("Warning: Failed to create top-level comment: %v", err) 253 continue 254 } 255 256 topLevelCommentsCreated = append(topLevelCommentsCreated, comment) 257 258 // Update post comment count 259 if err := updateCommentCount(db, postURI, true); err != nil { 260 log.Printf("Warning: Failed to update post comment count: %v", err) 261 } 262 263 // Small delay to avoid timestamp collisions 264 time.Sleep(10 * time.Millisecond) 265 } 266 267 log.Printf("Created %d top-level comments", len(topLevelCommentsCreated)) 268 269 // Create first-level replies (replies to top-level comments) 270 log.Println("\n=== Creating First-Level Replies ===") 271 firstLevelReplies := make([]*Comment, 0) 272 273 for i, parentComment := range topLevelCommentsCreated { 274 // 70% chance of having replies (NBA threads get lots of engagement) 275 if rand.Float64() > 0.7 { 276 continue 277 } 278 279 // 1-4 replies per comment 280 numReplies := 1 + rand.Intn(4) 281 for j := 0; j < numReplies && len(replyComments) > 0; j++ { 282 userIdx := (i*3 + j + len(topLevelCommentsCreated)) % len(users) 283 user := users[userIdx] 284 content := replyComments[rand.Intn(len(replyComments))] 285 createdAt := parentComment.CreatedAt.Add(time.Duration(3+rand.Intn(8)) * time.Minute) 286 287 comment, err := createComment(db, user, content, parentComment.URI, parentComment.CID, createdAt) 288 if err != nil { 289 log.Printf("Warning: Failed to create first-level reply: %v", err) 290 continue 291 } 292 293 firstLevelReplies = append(firstLevelReplies, comment) 294 295 // Update parent comment reply count 296 if err := updateCommentCount(db, parentComment.URI, false); err != nil { 297 log.Printf("Warning: Failed to update comment reply count: %v", err) 298 } 299 300 time.Sleep(10 * time.Millisecond) 301 } 302 } 303 304 log.Printf("Created %d first-level replies", len(firstLevelReplies)) 305 306 // Create second-level replies (replies to replies) - deep threads 307 log.Println("\n=== Creating Second-Level Replies ===") 308 secondLevelCount := 0 309 310 for i, parentComment := range firstLevelReplies { 311 // 50% chance of having deep replies (NBA drama threads go DEEP) 312 if rand.Float64() > 0.5 { 313 continue 314 } 315 316 // 1-2 deep replies 317 numReplies := 1 + rand.Intn(2) 318 for j := 0; j < numReplies && len(deepReplyComments) > 0; j++ { 319 userIdx := (i*2 + j + len(topLevelCommentsCreated) + len(firstLevelReplies)) % len(users) 320 user := users[userIdx] 321 content := deepReplyComments[rand.Intn(len(deepReplyComments))] 322 createdAt := parentComment.CreatedAt.Add(time.Duration(2+rand.Intn(5)) * time.Minute) 323 324 _, err := createComment(db, user, content, parentComment.URI, parentComment.CID, createdAt) 325 if err != nil { 326 log.Printf("Warning: Failed to create second-level reply: %v", err) 327 continue 328 } 329 330 secondLevelCount++ 331 332 // Update parent comment reply count 333 if err := updateCommentCount(db, parentComment.URI, false); err != nil { 334 log.Printf("Warning: Failed to update comment reply count: %v", err) 335 } 336 337 time.Sleep(10 * time.Millisecond) 338 } 339 } 340 341 log.Printf("Created %d second-level replies", secondLevelCount) 342 343 // Print summary 344 totalComments := len(topLevelCommentsCreated) + len(firstLevelReplies) + secondLevelCount 345 log.Println("\n=== Summary ===") 346 log.Printf("Total users created: %d", len(users)) 347 log.Printf("Total comments created: %d", totalComments) 348 log.Printf(" - Top-level comments: %d", len(topLevelCommentsCreated)) 349 log.Printf(" - First-level replies: %d", len(firstLevelReplies)) 350 log.Printf(" - Second-level replies: %d", secondLevelCount) 351 log.Println("\nDone! Check the NBACentral post for the brothers drama comments.") 352}