package notice import ( "fmt" "net/url" "strings" ) func Text(content string) Message { return Message{Type: MessageText, Content: content} } func ColorText(segments ...TextSegment) Message { return Message{Type: MessageColorText, Segments: segments} } func Markdown(title, content string) Message { return Message{Type: MessageMarkdown, Title: title, Content: content} } func Card(content CardContent) Message { return Message{Type: MessageCard, Card: &content} } func validateMessage(message Message) error { switch message.Type { case MessageText: if strings.TrimSpace(message.Content) == "" { return fmt.Errorf("%w: text content is empty", ErrInvalidMessage) } case MessageMarkdown: if strings.TrimSpace(message.Content) == "" { return fmt.Errorf("%w: markdown content is empty", ErrInvalidMessage) } case MessageColorText: nonEmpty := false for _, segment := range message.Segments { if strings.TrimSpace(segment.Text) != "" { nonEmpty = true } if !validColor(segment.Color) { return fmt.Errorf("%w: unsupported color %q", ErrInvalidMessage, segment.Color) } } if !nonEmpty { return fmt.Errorf("%w: color text segments are empty", ErrInvalidMessage) } case MessageCard: if message.Card == nil { return fmt.Errorf("%w: card content is nil", ErrInvalidMessage) } if strings.TrimSpace(message.Card.Title) == "" && strings.TrimSpace(message.Card.Markdown) == "" { return fmt.Errorf("%w: card title and markdown are empty", ErrInvalidMessage) } if !validTheme(message.Card.Theme) { return fmt.Errorf("%w: unsupported card theme %q", ErrInvalidMessage, message.Card.Theme) } for _, action := range message.Card.Actions { parsed, err := url.ParseRequestURI(strings.TrimSpace(action.URL)) if strings.TrimSpace(action.Text) == "" || err != nil || parsed.Scheme != "https" || parsed.Host == "" { return fmt.Errorf("%w: card action requires text and HTTPS URL", ErrInvalidMessage) } } default: return fmt.Errorf("%w: unsupported message type %q", ErrInvalidMessage, message.Type) } return nil } func validColor(color Color) bool { switch color { case "", ColorDefault, ColorInfo, ColorSuccess, ColorWarning, ColorDanger, ColorMuted: return true default: return false } } func validTheme(theme CardTheme) bool { switch theme { case "", CardThemeDefault, CardThemeBlue, CardThemeWathet, CardThemeTurquoise, CardThemeGreen, CardThemeYellow, CardThemeOrange, CardThemeRed, CardThemeCarmine, CardThemeViolet, CardThemePurple, CardThemeIndigo, CardThemeGrey: return true default: return false } }