package nostr import ( "crypto/sha256" "encoding/hex" "encoding/json" "fmt" ) // Event represents a Nostr event as defined in NIP-01. type Event struct { ID string `json:"id"` PubKey string `json:"pubkey"` CreatedAt int64 `json:"created_at"` Kind int `json:"kind"` Tags Tags `json:"tags"` Content string `json:"content"` Sig string `json:"sig"` } // Serialize returns the canonical JSON serialization of the event for ID computation. // Format: [0, "pubkey", created_at, kind, tags, "content"] func (e *Event) Serialize() []byte { // Use json.Marshal for proper escaping of content and tags arr := []interface{}{ 0, e.PubKey, e.CreatedAt, e.Kind, e.Tags, e.Content, } data, _ := json.Marshal(arr) return data } // ComputeID calculates the SHA256 hash of the serialized event. // Returns the 64-character hex-encoded ID. func (e *Event) ComputeID() string { serialized := e.Serialize() hash := sha256.Sum256(serialized) return hex.EncodeToString(hash[:]) } // SetID computes and sets the event ID. func (e *Event) SetID() { e.ID = e.ComputeID() } // CheckID verifies that the event ID matches the computed ID. func (e *Event) CheckID() bool { return e.ID == e.ComputeID() } // MarshalJSON implements json.Marshaler with empty tags as [] instead of null. func (e Event) MarshalJSON() ([]byte, error) { type eventAlias Event ea := eventAlias(e) if ea.Tags == nil { ea.Tags = Tags{} } return json.Marshal(ea) } // String returns a JSON representation of the event for debugging. func (e *Event) String() string { data, err := json.MarshalIndent(e, "", " ") if err != nil { return fmt.Sprintf("", err) } return string(data) }