diff --git a/pkg/services/ntfy/ntfy.go b/pkg/services/ntfy/ntfy.go index 32e8d69d..ceb4a8da 100644 --- a/pkg/services/ntfy/ntfy.go +++ b/pkg/services/ntfy/ntfy.go @@ -30,6 +30,19 @@ func (service *Service) Send(message string, params *types.Params) error { return err } + // Prevent enabling templated messages without custom data + if config.Template && message == "" { + return fmt.Errorf("body must be set if template is enabled") + } + + if config.Template && config.Message == "" && config.Title == "" { + return fmt.Errorf("at least title or message must be set if template is enabled") + } + + if config.Message != "" && !config.Template { + return fmt.Errorf("message should not be filled if template is disabled, use body instead") + } + if err := service.sendAPI(config, message); err != nil { return fmt.Errorf("failed to send ntfy notification: %w", err) } @@ -74,6 +87,10 @@ func (service *Service) sendAPI(config *Config, message string) error { if !config.Firebase { headers.Add("Firebase", "no") } + if config.Template { + headers.Add("Template", "yes") + headers.Add("Message", config.Message) + } if err := jsonClient.Post(config.GetAPIURL(), request, &response); err != nil { if jsonClient.ErrorResponse(err, &response) { diff --git a/pkg/services/ntfy/ntfy_config.go b/pkg/services/ntfy/ntfy_config.go index 895c83da..728e859b 100644 --- a/pkg/services/ntfy/ntfy_config.go +++ b/pkg/services/ntfy/ntfy_config.go @@ -27,6 +27,8 @@ type Config struct { Icon string `key:"icon" optional:"" desc:"URL to use as notification icon"` Cache bool `key:"cache" default:"yes" desc:"Cache messages"` Firebase bool `key:"firebase" default:"yes" desc:"Send to firebase"` + Template bool `key:"template" default:"no" desc:"Use message and title as template"` + Message string `key:"message" optional:"" desc:"Message, to be used only then template is set to true"` } // Enums implements types.ServiceConfig diff --git a/pkg/services/ntfy/ntfy_test.go b/pkg/services/ntfy/ntfy_test.go index 1719433c..3887d90b 100644 --- a/pkg/services/ntfy/ntfy_test.go +++ b/pkg/services/ntfy/ntfy_test.go @@ -71,16 +71,65 @@ var _ = Describe("the ntfy service", func() { Priority: 3, Firebase: true, Cache: true, + Template: false, + Message: "", })) }) }) + + When("validate that template behaves correctly", func() { + BeforeEach(func() { + httpmock.Activate() + }) + AfterEach(func() { + httpmock.DeactivateAndReset() + }) + + It("should not allow templated messages if message and title are not set", func() { + serviceURL := testutils.URLMust("ntfy://hostname/topic?template=true&message=&title=hello") + Expect(service.Initialize(serviceURL, logger)).ShouldNot(HaveOccurred()) + Expect(service.Send("", nil)).Should(HaveOccurred()) + }) + It("should allow templated messages if title is set", func() { + serviceURL := testutils.URLMust("ntfy://:devicekey@hostname/topic?template=true&message=&title=hello") + Expect(service.Initialize(serviceURL, logger)).ShouldNot(HaveOccurred()) + + httpmock.RegisterResponder("POST", service.config.GetAPIURL(), testutils.JSONRespondMust(200, apiResponse{ + Code: http.StatusOK, + Message: "OK", + })) + Expect(service.Send("{}", nil)).To(Succeed()) + }) + It("should allow templated messages if message is set", func() { + serviceURL := testutils.URLMust("ntfy://:devicekey@hostname/topic?template=true&message=hello&title=") + Expect(service.Initialize(serviceURL, logger)).ShouldNot(HaveOccurred()) + + httpmock.RegisterResponder("POST", service.config.GetAPIURL(), testutils.JSONRespondMust(200, apiResponse{ + Code: http.StatusOK, + Message: "OK", + })) + Expect(service.Send("{}", nil)).To(Succeed()) + }) + It("should not allow templated messages if body is empty", func() { + serviceURL := testutils.URLMust("ntfy://hostname/topic?template=true&message=hello&title=hello") + Expect(service.Initialize(serviceURL, logger)).ShouldNot(HaveOccurred()) + Expect(service.Send("", nil)).Should(HaveOccurred()) + }) + It("should validate that message is empty if template is disabled", func() { + serviceURL := testutils.URLMust("ntfy://hostname/topic?template=false&message=test") + Expect(service.Initialize(serviceURL, logger)).ShouldNot(HaveOccurred()) + Expect(service.Send("test", nil)).Should(HaveOccurred()) + }) + }) + When("parsing the configuration URL", func() { It("should be identical after de-/serialization", func() { - testURL := "ntfy://user:pass@example.com:2225/topic?cache=No&click=CLICK&firebase=No&icon=ICON&priority=Max&scheme=http&title=TITLE" + testURL := "ntfy://user:pass@example.com:2225/topic?cache=No&click=CLICK&firebase=No&icon=ICON&priority=Max&scheme=http&template=Yes&title=TITLE" config := &Config{} pkr := format.NewPropKeyResolver(config) Expect(config.setURL(&pkr, testutils.URLMust(testURL))).To(Succeed(), "verifying") Expect(config.GetURL().String()).To(Equal(testURL)) + Expect(config.Template).To(Equal(true)) }) }) }) @@ -132,7 +181,7 @@ var _ = Describe("the ntfy service", func() { testutils.TestConfigSetDefaultValues(&Config{}) testutils.TestConfigGetEnumsCount(&Config{}, 1) - testutils.TestConfigGetFieldsCount(&Config{}, 15) + testutils.TestConfigGetFieldsCount(&Config{}, 17) }) }) Describe("the service instance", func() {