A community based topic aggregation platform built on atproto
1package utils 2 3import ( 4 "database/sql" 5 "strings" 6 "time" 7) 8 9// ExtractRKeyFromURI extracts the record key from an AT-URI 10// Format: at://did/collection/rkey -> rkey 11func ExtractRKeyFromURI(uri string) string { 12 parts := strings.Split(uri, "/") 13 if len(parts) >= 4 { 14 return parts[len(parts)-1] 15 } 16 return "" 17} 18 19// ExtractCollectionFromURI extracts the collection from an AT-URI 20// Format: at://did/collection/rkey -> collection 21// 22// Returns: 23// - Collection name (e.g., "social.coves.community.comment") if URI is well-formed 24// - Empty string if URI is malformed or doesn't contain a collection segment 25// 26// Note: Empty string indicates "unknown/unsupported collection" and should be 27// handled as an invalid or unparseable URI by the caller. Callers should validate 28// the return value before using it for database queries or business logic. 29func ExtractCollectionFromURI(uri string) string { 30 withoutScheme := strings.TrimPrefix(uri, "at://") 31 parts := strings.Split(withoutScheme, "/") 32 if len(parts) >= 2 { 33 return parts[1] 34 } 35 return "" 36} 37 38// StringFromNull converts sql.NullString to string 39// Returns empty string if the NullString is not valid 40func StringFromNull(ns sql.NullString) string { 41 if ns.Valid { 42 return ns.String 43 } 44 return "" 45} 46 47// ParseCreatedAt extracts and parses the createdAt timestamp from an atProto record 48// Falls back to time.Now() if the field is missing or invalid 49// This preserves chronological ordering during Jetstream replays and backfills 50func ParseCreatedAt(record map[string]interface{}) time.Time { 51 if record == nil { 52 return time.Now() 53 } 54 55 createdAtStr, ok := record["createdAt"].(string) 56 if !ok || createdAtStr == "" { 57 return time.Now() 58 } 59 60 // atProto uses RFC3339 format for datetime fields 61 createdAt, err := time.Parse(time.RFC3339, createdAtStr) 62 if err != nil { 63 // Fallback to now if parsing fails 64 return time.Now() 65 } 66 67 return createdAt 68}