Fixes #753
Problem
The Discord and NTFY handlers in SendNotificationAsync (NotificationService.Webhooks.cs) do not return after handling the notification, so execution falls through to the generic webhook sender and every Discord/NTFY notification is posted a second time.
For Discord, the duplicate is built by CreateDiscordPayload; when the configured base URL is not an absolute http(s) URL (e.g. the default UrlBase of "/"), its embed thumbnail is a relative path, which the Discord API rejects with 400 {"embeds": ["0"]} — so the log shows a delivery failure on every notification even though the first (Discord-specific) send succeeded. When the payload is valid, the channel simply receives two copies.
The Pushover and Telegram handlers already return and are unaffected.
Fix
Add the missing return; at the end of the Discord and NTFY branches, making each service-specific handler terminal for URLs it handles — consistent with the existing Pushover/Telegram behavior.
Tests
Adds NotificationServiceSingleDispatchTests with two regression tests asserting exactly one HTTP request is made per Discord/NTFY notification (using the same mocked-HttpMessageHandler pattern as the existing NotificationServiceTests).
Verified the tests fail before the fix and pass after:
WITHOUT fix: Failed! - Failed: 2, Passed: 0 (two HTTP requests observed per notification)
WITH fix: Passed! - Failed: 0, Passed: 2
Full notification test group (--filter FullyQualifiedName~NotificationService, 13 tests) passes.
Also verified end-to-end against a live Discord webhook on a dev instance: one message delivered (with cover attachment), no Sending Generic POST follow-up, no 400.
🤖 Generated with Claude Code
Automated Canary build