// Package webhook provides Zulip webhook implementation package webhook import ( "fmt" "net/http" "net/url" "os" "strings" "time" "go-eve-pi/options" logger "git.site.quack-lab.dev/dave/cylogger" ) // ZulipWebhook implements WebhookInterface for Zulip type ZulipWebhook struct { url string email string token string client *http.Client } // NewZulipWebhook creates a new Zulip webhook client func NewZulipWebhook(url, email, token string) *ZulipWebhook { logger.Info("Zulip webhook client initialized with email: %s", email) // Parse HTTP timeout ONCE at initialization httpTimeout, err := time.ParseDuration(options.GlobalOptions.HTTPTimeout) if err != nil { logger.Error("Invalid HTTP timeout duration %s: %v", options.GlobalOptions.HTTPTimeout, err) os.Exit(1) } return &ZulipWebhook{ url: url, email: email, token: token, client: &http.Client{ Timeout: httpTimeout, }, } } // Post sends a message to Zulip func (z *ZulipWebhook) Post(channel, topic, message string) error { logger.Debug("Sending Zulip message to channel: %s, topic: %s", channel, topic) data := url.Values{} data.Set("type", "stream") data.Set("to", channel) data.Set("topic", topic) data.Set("content", message) req, err := http.NewRequest("POST", z.url, strings.NewReader(data.Encode())) if err != nil { return fmt.Errorf("failed to create zulip request: %w", err) } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.SetBasicAuth(z.email, z.token) resp, err := z.client.Do(req) if err != nil { return fmt.Errorf("failed to send zulip message: %w", err) } defer resp.Body.Close() if resp.StatusCode < 200 || resp.StatusCode >= 300 { return fmt.Errorf("zulip request failed with status: %d", resp.StatusCode) } logger.Info("Zulip message sent successfully to channel: %s, topic: %s", channel, topic) return nil }