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