package models import ( "database/sql" "encoding/json" "fmt" "time" ) // Bot represents a Nostr posting bot type Bot struct { ID int64 `db:"id" json:"id"` Pubkey string `db:"pubkey" json:"pubkey"` EncryptedPrivkey string `db:"encrypted_privkey" json:"encrypted_privkey,omitempty"` Name string `db:"name" json:"name"` DisplayName string `db:"display_name" json:"display_name"` Bio string `db:"bio" json:"bio"` Nip05 string `db:"nip05" json:"nip05"` ZapAddress string `db:"zap_address" json:"zap_address"` ProfilePicture string `db:"profile_picture" json:"profile_picture"` Banner string `db:"banner" json:"banner"` Website sql.NullString `db:"website" json:"website,omitempty"` // Changed to sql.NullString to handle NULL values // Custom JSON marshaling is handled in MarshalJSON/UnmarshalJSON methods CreatedAt time.Time `db:"created_at" json:"created_at"` OwnerPubkey string `db:"owner_pubkey" json:"owner_pubkey"` // The following are not stored in the database PostConfig *PostConfig `json:"post_config,omitempty"` MediaConfig *MediaConfig `json:"media_config,omitempty"` Relays []*Relay `json:"relays,omitempty"` } // PostConfig represents the posting configuration for a bot type PostConfig struct { ID int64 `db:"id" json:"id"` BotID int64 `db:"bot_id" json:"-"` Hashtags string `db:"hashtags" json:"hashtags"` // JSON array stored as string IntervalMinutes int `db:"interval_minutes" json:"interval_minutes"` PostTemplate string `db:"post_template" json:"post_template"` Enabled bool `db:"enabled" json:"enabled"` } // MediaConfig represents the media upload configuration for a bot type MediaConfig struct { ID int64 `db:"id" json:"id"` BotID int64 `db:"bot_id" json:"-"` PrimaryService string `db:"primary_service" json:"primary_service"` // "nip94" or "blossom" FallbackService string `db:"fallback_service" json:"fallback_service"` Nip94ServerURL string `db:"nip94_server_url" json:"nip94_server_url"` BlossomServerURL string `db:"blossom_server_url" json:"blossom_server_url"` } // Relay represents a Nostr relay configuration type Relay struct { ID int64 `db:"id" json:"id"` BotID int64 `db:"bot_id" json:"-"` URL string `db:"url" json:"url"` Read bool `db:"read" json:"read"` Write bool `db:"write" json:"write"` OwnerPubkey string `db:"owner_pubkey" json:"owner_pubkey,omitempty"` // Add this field } // Post represents a post made by the bot type Post struct { ID int64 `db:"id" json:"id"` BotID int64 `db:"bot_id" json:"bot_id"` ContentFilename string `db:"content_filename" json:"content_filename"` MediaURL string `db:"media_url" json:"media_url"` EventID string `db:"event_id" json:"event_id"` Status string `db:"status" json:"status"` // "pending", "posted", "failed" CreatedAt time.Time `db:"created_at" json:"created_at"` Error string `db:"error" json:"error,omitempty"` } // MarshalJSON handles custom JSON marshaling for Bot, especially for sql.NullString fields func (b Bot) MarshalJSON() ([]byte, error) { type Alias Bot // Create an alias to avoid infinite recursion // Create a copy of the bot to modify for JSON bot := &struct { Website interface{} `json:"website,omitempty"` *Alias }{ Alias: (*Alias)(&b), } // Handle the sql.NullString field specifically if b.Website.Valid { bot.Website = b.Website.String } else { bot.Website = nil } return json.Marshal(bot) } // UnmarshalJSON handles custom JSON unmarshaling for Bot, especially for sql.NullString fields func (b *Bot) UnmarshalJSON(data []byte) error { type Alias Bot // Create an alias to avoid infinite recursion // Create a proxy structure with website as interface{} aux := &struct { Website interface{} `json:"website,omitempty"` *Alias }{ Alias: (*Alias)(b), } if err := json.Unmarshal(data, &aux); err != nil { return err } // Handle possible types for the website field if aux.Website == nil { b.Website = sql.NullString{Valid: false} } else { switch v := aux.Website.(type) { case string: b.Website = sql.NullString{String: v, Valid: true} case map[string]interface{}: // Handle specific JSON format for sql.NullString if str, ok := v["String"].(string); ok { valid := true if v, ok := v["Valid"].(bool); ok { valid = v } b.Website = sql.NullString{String: str, Valid: valid} } case nil: b.Website = sql.NullString{Valid: false} default: return fmt.Errorf("unsupported type for Website: %T", v) } } return nil }