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}