⛳ alerts for any ctfd instance via ntfy
1package clients 2 3import ( 4 "bytes" 5 "crypto/tls" 6 "encoding/json" 7 "fmt" 8 "io" 9 "net/http" 10 "strings" 11 "time" 12) 13 14// NtfyMessage represents a notification message to be sent via ntfy 15type NtfyMessage struct { 16 Topic string `json:"topic"` 17 Message string `json:"message,omitempty"` 18 Title string `json:"title,omitempty"` 19 Tags []string `json:"tags,omitempty"` 20 Priority int `json:"priority,omitempty"` 21 Click string `json:"click,omitempty"` 22 Actions []map[string]any `json:"actions,omitempty"` 23 Attach string `json:"attach,omitempty"` 24 Filename string `json:"filename,omitempty"` 25 Markdown bool `json:"markdown,omitempty"` 26 Icon string `json:"icon,omitempty"` 27 Email string `json:"email,omitempty"` 28 Call string `json:"call,omitempty"` 29 Delay string `json:"delay,omitempty"` 30} 31 32// NtfyClient represents a client for sending notifications via ntfy.sh 33type NtfyClient struct { 34 Topic string 35 ServerURL string 36 HTTPClient *http.Client 37 // Optional authentication token 38 AccessToken string 39} 40 41// NewNtfyClient creates a new ntfy client with the specified topic and server URL. 42// It configures an HTTP client with a 10-second timeout and insecure TLS verification. 43func NewNtfyClient(topic, serverURL string, accessToken string) *NtfyClient { 44 serverURL = strings.TrimSuffix(serverURL, "/") 45 if serverURL == "" { 46 serverURL = "https://ntfy.sh" 47 } 48 49 return &NtfyClient{ 50 Topic: topic, 51 ServerURL: serverURL, 52 AccessToken: accessToken, 53 HTTPClient: &http.Client{ 54 Timeout: 10 * time.Second, 55 Transport: &http.Transport{ 56 TLSClientConfig: &tls.Config{ 57 InsecureSkipVerify: true, 58 }, 59 }, 60 }, 61 } 62} 63 64// NewMessage creates a new NtfyMessage with the specified message content 65func (c *NtfyClient) NewMessage(messageText string) *NtfyMessage { 66 return &NtfyMessage{ 67 Topic: c.Topic, 68 Message: messageText, 69 } 70} 71 72// SendMessage sends a structured NtfyMessage 73func (c *NtfyClient) SendMessage(msg *NtfyMessage) error { 74 // Ensure topic is set 75 if msg.Topic == "" { 76 msg.Topic = c.Topic 77 } 78 79 payload, err := json.Marshal(msg) 80 if err != nil { 81 return fmt.Errorf("error marshaling message: %v", err) 82 } 83 84 req, err := http.NewRequest("POST", c.ServerURL, bytes.NewBuffer(payload)) 85 if err != nil { 86 return fmt.Errorf("error creating request: %v", err) 87 } 88 89 req.Header.Add("Content-Type", "application/json") 90 91 if c.AccessToken != "" { 92 req.Header.Add("Authorization", "Bearer "+c.AccessToken) 93 } 94 95 resp, err := c.HTTPClient.Do(req) 96 if err != nil { 97 return fmt.Errorf("error executing request: %v", err) 98 } 99 defer resp.Body.Close() 100 101 if resp.StatusCode < 200 || resp.StatusCode >= 300 { 102 body, _ := io.ReadAll(resp.Body) 103 return fmt.Errorf("error response (status %d): %s", resp.StatusCode, string(body)) 104 } 105 106 return nil 107}