go-notificationgo-notification
Features

Message Builders

Fluent, chainable builders for every channel. Minimal surface area, maximum readability.

Every channel exposes a message builder via NewMessage(). The builders are designed to be:

  • Chainable — each method returns the builder so you can write it as one expression.
  • Channel-specificmail.Message has SetSubject(), whatsapp.Message has SetImage(), etc. No leaky common base.
  • Validation-light — builders don't try to validate upstream provider rules (that's the driver's job). They only guard against obvious local mistakes (nil payload, empty required fields).

Message types live under github.com/gopackx/go-notification/channel/<family>. Slack, Telegram, Discord, and Teams all share the single chat family — there is no per-provider message type.

Mail

The Laravel-style builder (SetGreeting / Line / Action / SetSalutation) auto-renders to a clean HTML template and a plain-text fallback at send time:

main.go
import "github.com/gopackx/go-notification/channel/mail"

mail.NewMessage().
    SetSubject("Your order shipped").
    SetGreeting("Hi, Dani").                     // optional
    Line("Your order A-1024 is on its way.").
    Action("Track package", "https://...").      // renders as button
    Line("Thanks for shopping with us.").
    SetSalutation("— The team")                  // optional

Sender, recipients, addressing

SetFrom, AddTo, AddCC, AddBCC, and AddReplyTo each take an address plus an optional display name. You can call the Add* variants multiple times to build a list:

main.go
mail.NewMessage().
    SetFrom("noreply@example.com", "Example").
    AddTo("dani@example.com", "Dani").
    AddCC("ops@example.com").
    AddBCC("audit@example.com").
    AddReplyTo("support@example.com", "Support")

If you don't set To, the channel falls back to RouteNotificationFor("mail").

Raw HTML and plain text

For full control, set the body directly. Provide both SetText and SetHTML so clients that prefer plain text get a usable version:

main.go
mail.NewMessage().
    SetSubject("Welcome").
    SetText("Welcome — open https://app.example.com/dashboard to get started.").
    SetHTML(renderedHTML)

See Email Templates for mail.TemplateRenderer (renders a Go html/template into Message.HTML).

Attachments

Attach takes a single mail.Attachment struct, not three positional args:

main.go
mail.NewMessage().
    Attach(mail.Attachment{
        Filename:    "invoice.pdf",
        ContentType: "application/pdf",
        Data:        pdfBytes,
    })

For inline images (CID references in HTML), set Inline and ContentID:

main.go
mail.NewMessage().
    SetHTML(`<p>Logo: <img src="cid:logo"></p>`).
    Attach(mail.Attachment{
        Filename:    "logo.png",
        ContentType: "image/png",
        Data:        logoBytes,
        Inline:      true,
        ContentID:   "logo",
    })

Priority

SetPriority accepts one of three constants — providers that support priority (Mailgun, SendGrid, …) map them to their headers; others ignore the value:

main.go
mail.NewMessage().SetPriority(mail.PriorityHigh)
// mail.PriorityNormal (default), mail.PriorityHigh, mail.PriorityLow

Headers, tags, metadata

For provider-side analytics, suppression lists, and routing:

main.go
mail.NewMessage().
    AddHeader("X-Campaign-ID", campaignID).   // raw SMTP/HTTP header
    AddTag("welcome", "onboarding").           // appears in Mailgun/SendGrid analytics
    SetMetadata("user_id", userID).            // key/value provider metadata
    SetMetadata("plan", "pro")

AddTag is variadic — pass several tags in one call. SetMetadata is key-by-key. Both are best-effort: drivers only forward what their provider supports.

WhatsApp

main.go
import "github.com/gopackx/go-notification/channel/whatsapp"

whatsapp.NewMessage().
    SetText("Hi! Your OTP is *" + code + "*. Valid 5 minutes.")

With media — SetImage takes the URL and a caption together:

main.go
whatsapp.NewMessage().
    SetImage("https://cdn.example.com/invoice.jpg", "Your invoice")

Template (for Twilio/Meta official APIs). SetTemplate takes the template name, language, and any positional parameters:

main.go
whatsapp.NewMessage().
    SetTemplate("otp_v2", "en", code).
    SetText("Your code: " + code) // fallback text

SMS

main.go
import "github.com/gopackx/go-notification/channel/sms"

sms.NewMessage().SetText("Your OTP: " + code)

SMS is deliberately minimal — no rich content, no attachments. Beyond SetText there's only SetTo and SetFrom. Anything more is a different channel.

Push (FCM)

main.go
import "github.com/gopackx/go-notification/channel/push"

push.NewMessage().
    SetTitle("Order shipped").
    SetBody("Track it now").
    SetBadge(1).
    SetSound("default").
    SetData("order_id", "A-1024")

The push payload is provider-agnostic: target with SetToken / SetTokens / SetTopic, and pass custom key/value pairs through SetData (values are strings). Platform-specific config is left to the driver.

Chat (Slack, Telegram, Discord, Teams)

All four chat providers use one chat.Message. The common SetText works everywhere; provider-specific richness is added with dedicated methods and fields.

main.go
import "github.com/gopackx/go-notification/channel/chat"

chat.NewMessage().SetText("Deploy started")

Slack attachments:

main.go
chat.NewMessage().
    SetText("Deploy started").
    AddSlackAttachment(chat.SlackAttachment{
        Color: "good",
        Title: "Build",
        Fields: []chat.SlackField{
            {Title: "Service", Value: svc, Short: true},
            {Title: "Env", Value: env, Short: true},
        },
        Footer: "CI",
    })

Telegram uses SetParseMode and a couple of Telegram-specific fields set directly on the struct:

main.go
msg := chat.NewMessage().
    SetText("*Alert*\nBuild failed on `main`").
    SetParseMode("MarkdownV2")
msg.TelegramDisableWebPreview = true

Discord embeds:

main.go
chat.NewMessage().
    SetText("Build failed").
    AddDiscordEmbed(chat.DiscordEmbed{
        Title: "CI #1234",
        Color: 0xFF0000,
        Fields: []chat.DiscordField{
            {Name: "Branch", Value: "main", Inline: true},
        },
    })

Teams uses the MessageCard schema via the TeamsCard field:

main.go
msg := chat.NewMessage().SetText("Deploy to prod")
msg.TeamsCard = map[string]any{
    "title": "Deploy",
    "text":  "Environment: prod",
}

Database

main.go
import "github.com/gopackx/go-notification/channel/database"

database.NewMessage().
    SetType("order.shipped").
    SetTitle("Order shipped").
    SetBody("Your order is on its way.").
    AddData("order_id", orderID)

Use AddData to attach payload keys one at a time, or SetData to replace the whole map at once.

Webhook

main.go
import "github.com/gopackx/go-notification/channel/webhook"

webhook.NewMessage().
    SetURL("https://hooks.example.com/notify").
    SetMethod("POST").
    SetJSON(map[string]any{"event": "order.shipped", "order_id": id}).
    AddHeader("X-Idempotency-Key", idempotencyKey)

Choose the body encoding with SetJSON, SetForm, or SetRaw.

Why fluent builders

The builders are optimized for reading a notification spec next to its sending code — what you type in ToMail() should match what the mail looks like without mental translation. Struct literals lose that property once you have more than three fields.