Nedo.AspNet.Messaging 1.0.0

dotnet add package Nedo.AspNet.Messaging --version 1.0.0
                    
NuGet\Install-Package Nedo.AspNet.Messaging -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Nedo.AspNet.Messaging" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Nedo.AspNet.Messaging" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Nedo.AspNet.Messaging" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Nedo.AspNet.Messaging --version 1.0.0
                    
#r "nuget: Nedo.AspNet.Messaging, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Nedo.AspNet.Messaging@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Nedo.AspNet.Messaging&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Nedo.AspNet.Messaging&version=1.0.0
                    
Install as a Cake Tool

Nedo.AspNet.Messaging

ASP.NET Core messaging library for multi-channel notifications. Send messages through Email, Slack, Webhook, Microsoft Teams, and Telegram with a unified API, fluent builders, and built-in resilience.

Features

Feature Description
5 Channels Email (SMTP), Slack, Microsoft Teams, Telegram, Webhook
Fluent Builders Typed, validated builders — no raw metadata dictionaries
Broadcast Send one message to multiple channels in parallel
Channel Fallback If primary channel fails, automatically try fallback channels
Polly Resilience Automatic retry (3x exponential) + circuit breaker on HTTP providers
Deduplication Idempotency key support to prevent duplicate sends
Attachments File attachments for Email channel
Health Checks ASP.NET Core IHealthCheck integration
Options Validation Fail-fast startup validation for provider configuration
Structured Logging Zero-allocation [LoggerMessage] source-generated logging
Extensible Add new channels by implementing IMessageSender

Table of Contents

Project Structure

├── src/
│   └── Nedo.AspNet.Messaging/
│       ├── Abstractions/               # IMessageSender, IMessagingService
│       ├── Builders/                   # Typed fluent builders per channel
│       ├── Extensions/                 # DI + health check registration
│       ├── Health/                     # MessagingHealthCheck
│       ├── Models/                     # MessageRequest, MessageResult, etc.
│       ├── Providers/
│       │   ├── Email/                  # SMTP provider
│       │   ├── Slack/                  # Slack incoming webhook
│       │   ├── Teams/                  # Teams Adaptive Card
│       │   ├── Telegram/              # Telegram Bot API
│       │   └── Webhook/               # Generic HTTP POST
│       ├── Services/                   # MessagingService, LogMessages
│       └── Validation/                 # Options validators (startup)
├── test/
│   └── Nedo.AspNet.Messaging.Tests/    # Unit tests (xUnit, 49 tests)
├── sample/
│   └── Nedo.AspNet.Messaging.Sample/   # Sample minimal API with Swagger
└── docs/                               # Per-channel setup guides

Getting Started

dotnet build       # Build
dotnet test        # Run tests
dotnet run --project sample/Nedo.AspNet.Messaging.Sample   # Run sample (Swagger at /swagger)

Installation

<ProjectReference Include="path/to/src/Nedo.AspNet.Messaging/Nedo.AspNet.Messaging.csproj" />

Or as a NuGet package:

dotnet add package Nedo.AspNet.Messaging

Configuration

Add to appsettings.json — only include the channels you need:

{
    "Messaging": {
        "Email": {
            "Host": "smtp.example.com",
            "Port": 587,
            "Username": "your-username",
            "Password": "your-password",
            "FromAddress": "noreply@example.com",
            "FromName": "My Application",
            "UseSsl": true
        },
        "Slack": {
            "WebhookUrl": "https://hooks.slack.com/services/T.../B.../xxx",
            "DefaultChannel": "#general",
            "Username": "NedoBot",
            "IconEmoji": ":robot_face:"
        },
        "Teams": {
            "WebhookUrl": "https://outlook.office.com/webhook/..."
        },
        "Telegram": {
            "BotToken": "123456:ABC-DEF...",
            "DefaultChatId": "987654321",
            "ParseMode": "HTML"
        },
        "Webhook": {
            "DefaultUrl": "https://api.example.com/webhook",
            "Headers": {
                "X-Api-Key": "your-api-key"
            }
        }
    }
}

Usage

1. Register Services

using Nedo.AspNet.Messaging.Extensions;

var builder = WebApplication.CreateBuilder(args);
var config = builder.Configuration;

builder.Services.AddMessaging()
    .AddEmailProvider(config.GetSection("Messaging:Email"))
    .AddSlackProvider(config.GetSection("Messaging:Slack"))
    .AddTeamsProvider(config.GetSection("Messaging:Teams"))
    .AddTelegramProvider(config.GetSection("Messaging:Telegram"))
    .AddWebhookProvider(config.GetSection("Messaging:Webhook"));

// Optional: health checks
builder.Services.AddHealthChecks()
    .AddMessagingHealthCheck();

You can also configure providers with an action delegate:

builder.Services.AddMessaging()
    .AddEmailProvider(opts =>
    {
        opts.Host = "smtp.example.com";
        opts.Port = 587;
        opts.FromAddress = "noreply@example.com";
        opts.UseSsl = true;
    });

2. Send Messages

Each channel has a typed fluent builder with validation:

using Nedo.AspNet.Messaging.Abstractions;
using Nedo.AspNet.Messaging.Builders;

public class NotificationService
{
    private readonly IMessagingService _messaging;

    public NotificationService(IMessagingService messaging)
    {
        _messaging = messaging;
    }

    public async Task NotifyUserAsync(string email, string message)
    {
        var request = new EmailMessageBuilder()
            .To(email)
            .Subject("Notification")
            .Body(message)
            .AsHtml()
            .Build();   // throws if To or Body is missing

        var result = await _messaging.SendAsync(request);

        if (!result.IsSuccess)
            Console.WriteLine($"Send failed: {result.ErrorMessage}");
    }
}

3. Check Supported Channels

var channels = _messaging.SupportedChannels;
// Returns: [Email, Slack, Teams, Telegram, Webhook]

Broadcast

Send one message to multiple channels in parallel:

var request = new EmailMessageBuilder()
    .To("user@example.com")
    .Subject("Critical Alert")
    .Body("Server is down!")
    .Build();

var result = await messaging.BroadcastAsync(
    [MessageChannel.Email, MessageChannel.Slack, MessageChannel.Telegram],
    request);

if (result.AllSucceeded)
    Console.WriteLine("All channels delivered");
else
    Console.WriteLine($"Failed: {string.Join(", ", result.FailedChannels)}");

BroadcastResult properties:

Property Type Description
AllSucceeded bool All channels succeeded
AnySucceeded bool At least one channel succeeded
Results IReadOnlyList<MessageResult> Per-channel results
SucceededChannels IEnumerable<MessageChannel> Channels that succeeded
FailedChannels IEnumerable<MessageChannel> Channels that failed

Channel Fallback

If the primary channel fails, automatically try fallback channels in order:

var request = new EmailMessageBuilder()
    .To("user@example.com")
    .Subject("Alert")
    .Body("Server is down!")
    .Build();

var result = await messaging.SendWithFallbackAsync(
    request,
    primary: MessageChannel.Slack,
    fallbacks: [MessageChannel.Email, MessageChannel.Telegram]);

// Tries Slack first → if it fails, tries Email → if it fails, tries Telegram

Deduplication

Prevent duplicate message sends using an idempotency key:

var request = new MessageRequest
{
    Channel = MessageChannel.Email,
    To = "user@example.com",
    Body = "Order #123 confirmed",
    IdempotencyKey = "order-123-confirmation" // unique key
};

await messaging.SendAsync(request); // sends
await messaging.SendAsync(request); // silently skipped (same key within 5 min)
  • Messages with the same IdempotencyKey + channel within a 5-minute window are considered duplicates
  • Duplicate sends return success without actually sending
  • Useful for retry scenarios at the application level

Attachments

Email supports file attachments via the builder:

var pdfBytes = await File.ReadAllBytesAsync("report.pdf");

var request = new EmailMessageBuilder()
    .To("user@example.com")
    .Subject("Monthly Report")
    .Body("Please find the report attached.")
    .Attach("report.pdf", pdfBytes, "application/pdf")
    .Attach("summary.csv", csvBytes, "text/csv")
    .Build();

You can also attach from a stream:

using var stream = File.OpenRead("large-file.zip");
builder.Attach("large-file.zip", stream, "application/zip");

Health Checks

Register the ASP.NET Core health check:

builder.Services.AddHealthChecks()
    .AddMessagingHealthCheck(name: "messaging", tags: new[] { "ready" });

app.MapHealthChecks("/health");

Response when healthy:

{
    "status": "Healthy",
    "description": "5 messaging channel(s) registered.",
    "data": {
        "registered_channels": "Email, Slack, Webhook, Teams, Telegram",
        "channel_count": 5
    }
}

Channels

Email (SMTP)

var request = new EmailMessageBuilder()
    .To("user@example.com")                // semicolon-separated for multiple
    .Subject("Weekly Report")
    .Body("<h1>Report</h1><p>Details...</p>")
    .AsHtml()
    .Cc("manager@example.com; lead@example.com")
    .Bcc("archive@example.com")
    .ReplyTo("support@example.com")
    .Priority(MessagePriority.High)
    .Build();
Property Type Default Description
Host string localhost SMTP server hostname
Port int 587 SMTP server port
Username string? null SMTP auth username
Password string? null SMTP auth password
FromAddress string noreply@example.com Sender email address
FromName string Nedo Messaging Sender display name
UseSsl bool true Enable SSL/TLS

Slack

var request = new SlackMessageBuilder()
    .Subject("Deploy Status")               // bold prefix
    .Body("Deployment completed :rocket:")
    .Channel("#deployments")
    .Username("DeployBot")
    .IconEmoji(":rocket:")
    .Build();
Property Type Default Description
WebhookUrl string "" Slack incoming webhook URL
DefaultChannel string? null Default channel
Username string? null Bot username
IconEmoji string? null Bot icon emoji

Microsoft Teams

var request = new TeamsMessageBuilder()
    .Subject("Build Failed")                // Adaptive Card title
    .Body("Build #1234 failed on branch main.")
    .WebhookUrl("https://...")              // optional override
    .Build();
Property Type Default Description
WebhookUrl string "" Teams incoming webhook URL

Telegram

var request = new TelegramMessageBuilder()
    .ChatId("123456789")                    // or use default from config
    .Subject("Alert")                       // bold prefix
    .Body("Server CPU is at 95%")
    .ParseMode("HTML")
    .Build();
Property Type Default Description
BotToken string "" Bot API token from @BotFather
DefaultChatId string? null Default chat/group ID
ParseMode string? null "HTML", "MarkdownV2", or empty

Webhook

var request = new WebhookMessageBuilder()
    .To("user-123")
    .Subject("Order Placed")
    .Body("Order #5678 has been placed.")
    .Url("https://api.partner.com/notify")  // optional override
    .Build();
Property Type Default Description
DefaultUrl string "" Default HTTP endpoint URL
Headers Dictionary<string, string> {} Default headers

Resilience (Polly)

All HTTP-based providers (Slack, Teams, Telegram, Webhook) automatically include:

  • Retry: 3 attempts with exponential backoff (2s → 4s → 8s) for transient HTTP errors (5xx, 408, network failures)
  • Circuit Breaker: Opens after 5 consecutive failures, stays open for 30s

No configuration needed — policies are applied at DI registration.

Adding a Custom Channel

  1. Add a value to the MessageChannel enum
  2. Create YourOptions class and YourMessageSender : IMessageSender
  3. Add an AddYourProvider() DI extension method
  4. Optionally create a typed YourMessageBuilder

See docs/06-extending.md for a full step-by-step example using SMS/Twilio.

Documentation

Detailed guides for each channel and advanced features are in the docs/ folder:

Doc Content
Getting Started
00-overview.md Architecture, features, quick start
01-email.md Gmail, SendGrid, SES, O365 setup, attachments
02-slack.md Webhook creation, mrkdwn formatting reference
03-teams.md Connectors, Power Automate, Adaptive Cards
04-telegram.md BotFather, Chat IDs, HTML/MarkdownV2 reference
05-webhook.md Custom headers, payload format, integration patterns
Advanced
06-extending.md Full custom channel walkthrough (SMS/Twilio example)
07-broadcast-and-fallback.md Multi-channel send, fallback chains, deduplication
08-resilience.md Polly retry, circuit breaker, customization
09-health-checks.md Health endpoint, Kubernetes probes, Docker
10-troubleshooting.md Common errors, debugging, FAQ

Architecture

┌─────────────────────────────────────────────────┐
│                Consumer Code                     │
│    (inject IMessagingService, use Builders)       │
└──────────────────┬──────────────────────────────┘
                   │ SendAsync / BroadcastAsync
                   ▼
┌─────────────────────────────────────────────────┐
│       MessagingService (router)                  │
│  ┌─────────────────────────────────────────┐    │
│  │  Polly: Retry + Circuit Breaker (HTTP)  │    │
│  └─────────────────────────────────────────┘    │
└──┬──────┬──────┬──────┬──────┬──────────────────┘
   │      │      │      │      │
   ▼      ▼      ▼      ▼      ▼
 Email  Slack  Webhook Teams Telegram
 SMTP   Hook   HTTP    Card  Bot API

Key components:

  • IMessagingService — consumer interface (SendAsync, BroadcastAsync, SupportedChannels)
  • IMessageSender — provider contract, one per channel
  • Builders — EmailMessageBuilder, SlackMessageBuilder, TeamsMessageBuilder, TelegramMessageBuilder, WebhookMessageBuilder
  • MessageRequest — channel-agnostic payload with Attachments and Metadata
  • MessageResult / BroadcastResult — delivery results
  • MessagingHealthCheck — ASP.NET Core IHealthCheck

License

MIT

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 144 3/2/2026