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// StringFromNull converts sql.NullString to string 20// Returns empty string if the NullString is not valid 21func StringFromNull(ns sql.NullString) string { 22 if ns.Valid { 23 return ns.String 24 } 25 return "" 26} 27 28// ParseCreatedAt extracts and parses the createdAt timestamp from an atProto record 29// Falls back to time.Now() if the field is missing or invalid 30// This preserves chronological ordering during Jetstream replays and backfills 31func ParseCreatedAt(record map[string]interface{}) time.Time { 32 if record == nil { 33 return time.Now() 34 } 35 36 createdAtStr, ok := record["createdAt"].(string) 37 if !ok || createdAtStr == "" { 38 return time.Now() 39 } 40 41 // atProto uses RFC3339 format for datetime fields 42 createdAt, err := time.Parse(time.RFC3339, createdAtStr) 43 if err != nil { 44 // Fallback to now if parsing fails 45 return time.Now() 46 } 47 48 return createdAt 49}