### Install SMTP Only Module Source: https://github.com/shyim/go-mailer/blob/main/docs/getting-started/modules.md Use 'go get' to install the main go-mailer package and the SMTP transport module. ```shell go get github.com/shyim/go-mailer go get github.com/shyim/go-mailer/transport/smtp ``` -------------------------------- ### Install SMTP and Sendmail Modules Source: https://github.com/shyim/go-mailer/blob/main/docs/getting-started/modules.md Install go-mailer along with both SMTP and Sendmail transport modules. ```shell go get github.com/shyim/go-mailer go get github.com/shyim/go-mailer/transport/smtp go get github.com/shyim/go-mailer/transport/sendmail ``` -------------------------------- ### Install Root Module Source: https://github.com/shyim/go-mailer/blob/main/docs/getting-started/installation.md Use 'go get' to install the main go-mailer module. This module provides core abstractions and transports like null and test. ```sh go get github.com/shyim/go-mailer ``` -------------------------------- ### Build Documentation Locally Source: https://github.com/shyim/go-mailer/blob/main/README.md Build the documentation site locally using Zensical. Ensure Zensical is installed or install it via pip. ```sh uvx zensical build ``` ```sh pip install zensical && zensical build ``` -------------------------------- ### Install with OpenTelemetry Module Source: https://github.com/shyim/go-mailer/blob/main/docs/getting-started/modules.md Install go-mailer, the SMTP transport, and the OpenTelemetry middleware module. ```shell go get github.com/shyim/go-mailer go get github.com/shyim/go-mailer/transport/smtp go get github.com/shyim/go-mailer/middleware/otelmw ``` -------------------------------- ### Install Go Mailer Transport Packages Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/dsn.md Before using a transport scheme, ensure its corresponding package is installed using `go get`. ```bash go get github.com/shyim/go-mailer/transport/smtp go get github.com/shyim/go-mailer/transport/sendmail ``` -------------------------------- ### Install go-mailer SES Transport Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/ses.md Install the SES transport module using go get. ```sh go get github.com/shyim/go-mailer/transport/ses ``` -------------------------------- ### Install Go Mailer Packages Source: https://github.com/shyim/go-mailer/blob/main/README.md Install the core go-mailer package and optional transport or middleware packages using go get. Requires Go 1.26+. ```sh go get github.com/shyim/go-mailer # core: MIME, null, composites, DSN, middleware go get github.com/shyim/go-mailer/transport/smtp # SMTP / ESMTP transport go get github.com/shyim/go-mailer/transport/sendmail go get github.com/shyim/go-mailer/transport/ses # Amazon SES (isolates the AWS SDK) go get github.com/shyim/go-mailer/middleware/otelmw # OpenTelemetry traces + metrics ``` -------------------------------- ### SMTP Submission with STARTTLS Source: https://github.com/shyim/go-mailer/blob/main/docs/reference/dsn-options.md Use this example for secure email submission where STARTTLS is required on the specified port. ```sh smtp://user:pass@smtp.example.com:587?require_tls=true ``` -------------------------------- ### Send an Email with Go-Mailer Source: https://github.com/shyim/go-mailer/blob/main/docs/getting-started/index.md This example demonstrates how to construct a new email message, configure an SMTP transport from a DSN, and send the message using the Mailer. Ensure the SMTP transport submodule is imported to register the smtp:// scheme. ```go package main import ( "context" "log" "github.com/shyim/go-mailer" "github.com/shyim/go-mailer/transport" _ "github.com/shyim/go-mailer/transport/smtp" // register the smtp:// scheme ) func main() { t, err := transport.FromDSN("smtp://user:pass@smtp.example.com:587", transport.Deps{}) if err != nil { log.Fatal(err) } msg := gomailer.NewMessage(). SetFrom(gomailer.MustAddress("alice@example.com", "Alice")), SetTo(gomailer.MustAddress("bob@example.com", "Bob")), SetSubject("Hello"), SetText([]byte("Hello, Bob!")) mailer := gomailer.NewMailer(t) if err := mailer.Send(context.Background(), msg, nil); err != nil { log.Fatalf("send failed: %v", err) } } ``` -------------------------------- ### Install OpenTelemetry Middleware Source: https://github.com/shyim/go-mailer/blob/main/docs/getting-started/installation.md Install the OpenTelemetry adapter module if you need to integrate with OpenTelemetry for tracing and metrics. This is an optional dependency. ```sh go get github.com/shyim/go-mailer/middleware/otelmw ``` -------------------------------- ### Composite DSN Syntax Examples Source: https://github.com/shyim/go-mailer/blob/main/docs/reference/dsn-options.md Demonstrates the syntax for failover and round-robin composite DSNs, which wrap multiple child DSNs separated by spaces. These can be nested recursively. ```text failover(smtp://a.example.com smtp://b.example.com) roundrobin(smtp://a.example.com smtp://b.example.com) ``` -------------------------------- ### Install OTLP Exporter Dependencies Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/observability.md Install the necessary OpenTelemetry exporter packages for gRPC. These are required for sending trace and metric data to an OTLP collector. ```bash go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc go get go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc go get go.opentelemetry.io/otel/sdk go.opentelemetry.io/otel/sdk/metric ``` -------------------------------- ### Send HTML Email with Attachment and DSN Source: https://github.com/shyim/go-mailer/blob/main/README.md This example shows how to send an email with both plain text and HTML bodies, includes an attachment, and configures the transport using a DSN string. The blank import registers the SMTP scheme for `transport.FromDSN`. ```go import ( "github.com/shyim/go-mailer" "github.com/shyim/go-mailer/transport" _ "github.com/shyim/go-mailer/transport/smtp" // registers smtp:// and smtps:// ) func sendReport(ctx context.Context, pdfBytes []byte) error { msg := gomailer.NewMessage(). SetFrom(gomailer.MustAddress("alice@example.com", "Alice")), SetTo(gomailer.MustAddress("bob@example.com", "Bob")), SetSubject("Your report"), SetText([]byte("See the attached report.")), SetHTML([]byte("
See the attached report.
")), Attach(gomailer.Attachment{ Filename: "report.pdf", ContentType: "application/pdf", Data: pdfBytes, }) // Build the transport from a DSN; the blank import above registers the scheme. tr, err := transport.FromDSN("smtp://alice:secret@smtp.example.com:587", transport.Deps{}) if err != nil { return err } return gomailer.NewMailer(tr).Send(ctx, msg, nil) } ``` -------------------------------- ### SMTP with Implicit TLS Source: https://github.com/shyim/go-mailer/blob/main/docs/reference/dsn-options.md Use this example for connections that establish an encrypted TLS connection immediately upon connecting to the server on the specified port. ```sh smtps://user:pass@smtp.example.com:465 ``` -------------------------------- ### Setup OTLP gRPC Exporter and Middleware Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/observability.md Configure OpenTelemetry trace and metric providers using OTLP gRPC exporters. This function returns middleware to wrap transports and a shutdown function to flush exporters. ```go package main import ( "context" "time" "github.com/shyim/go-mailer/middleware" "github.com/shyim/go-mailer/middleware/otelmw" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" sdkmetric "go.opentelemetry.io/otel/sdk/metric" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) func setupObservability(ctx context.Context) (middleware.Middleware, func(context.Context) error, error) { traceExp, err := otlptracegrpc.New(ctx) // honors OTEL_EXPORTER_OTLP_ENDPOINT if err != nil { return nil, nil, err } tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(traceExp)) metricExp, err := otlpmetricgrpc.New(ctx) if err != nil { return nil, nil, err } mp := sdkmetric.NewMeterProvider(sdkmetric.WithReader( sdkmetric.NewPeriodicReader(metricExp, sdkmetric.WithInterval(10*time.Second)), )) shutdown := func(ctx context.Context) error { _ = tp.Shutdown(ctx) return mp.Shutdown(ctx) } return otelmw.New(tp, mp), shutdown, nil } ``` -------------------------------- ### Install SMTP Transport Source: https://github.com/shyim/go-mailer/blob/main/docs/getting-started/installation.md Install the SMTP transport module separately if you need to send emails via SMTP. This allows for modular dependency management. ```sh go get github.com/shyim/go-mailer/transport/smtp ``` -------------------------------- ### Implicit TLS Connection Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/production.md Use this for establishing a secure SMTP connection with TLS enabled from the start, typically on port 465. ```go t := smtp.NewTransport("smtp.example.com", 465, true). SetUsername("user"). SetPassword("pass") ``` -------------------------------- ### Install Sendmail Transport Source: https://github.com/shyim/go-mailer/blob/main/docs/getting-started/installation.md Install the Sendmail transport module if you intend to use the system's sendmail command for email delivery. This is an alternative to network SMTP. ```sh go get github.com/shyim/go-mailer/transport/sendmail ``` -------------------------------- ### Update Go Mailer Submodules Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/production.md When using pre-1.0 versions, pin and update the root and submodule versions together using go get to ensure consistency. ```sh go get github.com/shyim/go-mailer@v0.x.y go get github.com/shyim/go-mailer/transport/smtp@v0.x.y ``` -------------------------------- ### Preview Documentation Locally Source: https://github.com/shyim/go-mailer/blob/main/CONTRIBUTING.md Use 'uvx zensical serve' for a live preview of the documentation site as you make changes. Use 'uvx zensical build' for a one-off build. ```sh uvx zensical serve # live preview uvx zensical build # one-off build into ./site ``` -------------------------------- ### Configure SES Transport with DSN Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/ses.md Set up the SES transport by blank-importing the module and using transport.FromDSN with a 'ses://' URI. The region can be specified in the DSN. ```go import ( "github.com/shyim/go-mailer/transport" _ "github.com/shyim/go-mailer/transport/ses" // registers ses:// ) tr, err := transport.FromDSN("ses://default?region=us-east-1", transport.Deps{}) ``` -------------------------------- ### Basic Email Sending with Go Mailer Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/sending-mail.md Demonstrates setting up a mailer with an SMTP transport and sending a simple email with text and HTML content. ```go package main import ( "context" "log" "github.com/shyim/go-mailer" "github.com/shyim/go-mailer/transport" _ "github.com/shyim/go-mailer/transport/smtp" ) func main() { t, err := transport.FromDSN("smtp://user:pass@smtp.example.com:587", transport.Deps{}) if err != nil { log.Fatal(err) } mailer := gomailer.NewMailer(t) defer mailer.Close() msg := gomailer.NewMessage(). SetFrom(gomailer.MustAddress("alice@example.com", "Alice")), SetTo(gomailer.MustAddress("bob@example.com", "Bob")), SetSubject("Hello"), SetText([]byte("Hello, Bob!")), SetHTML([]byte("Hello, Bob!
")) if err := mailer.Send(context.Background(), msg, nil); err != nil { log.Fatalf("send failed: %v", err) } } ``` -------------------------------- ### Send a Simple Email with SMTP Source: https://github.com/shyim/go-mailer/blob/main/docs/index.md This snippet demonstrates how to send a basic email using the SMTP transport. It requires setting up the transport with server details and credentials, then constructing and sending the message. ```go package main import ( "context" "log" "github.com/shyim/go-mailer" "github.com/shyim/go-mailer/transport/smtp" ) func main() { t t := smtp.NewTransport("smtp.example.com", 587, false). SetUsername("user").SetPassword("pass") msg := gomailer.NewMessage(). SetFrom(gomailer.MustAddress("alice@example.com", "Alice")ワクチン). SetTo(gomailer.MustAddress("bob@example.com", "Bob")ワクチン). SetSubject("Hello"). SetText([]byte("Hello, Bob!")) mailer := gomailer.NewMailer(t) defer mailer.Close() if err := mailer.Send(context.Background(), msg, nil); err != nil { log.Fatalf("send failed: %v", err) } } ``` -------------------------------- ### STARTTLS Connection Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/production.md This snippet demonstrates setting up an SMTP connection that opportunistically upgrades to TLS using STARTTLS, commonly on port 587. ```go t := smtp.NewTransport("smtp.example.com", 587, false). SetUsername("user"). SetPassword("pass") ``` -------------------------------- ### Retrieve Sent Message ID Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/production.md After sending a message, read the MessageID from the returned SendResult rather than re-serializing the message to get the ID that was actually sent. ```go sm, err := t.Send(ctx, msg, nil) if err == nil { log.Printf("sent %s", sm.MessageID()) } ``` -------------------------------- ### Initialize Mailer and Defer Close Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/sending-mail.md Initialize the mailer and defer the `mailer.Close()` call to ensure resources are released and the underlying transport's `QUIT` command is sent on shutdown. This is safe to call multiple times. ```go mailer := gomailer.NewMailer(t) defer mailer.Close() ``` -------------------------------- ### MIME Builder and Mailer Interface Source: https://github.com/shyim/go-mailer/blob/main/docs/concepts/architecture.md Illustrates the initial steps of creating a MIME message and how the Mailer interface wraps a Transport for sending. ```go NewMessage().SetFrom(...).SetTo(...).SetSubject(...) ``` ```go Send(ctx, msg RawMessage, envelope *Envelope) ``` -------------------------------- ### Set Plain Text and HTML Bodies Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/sending-mail.md Provide both plain text and HTML bodies to automatically create a `multipart/alternative` email. The text part is sent first, followed by the HTML part. ```go msg. SetText([]byte("Hello, Bob! View this email in an HTML client.")). SetHTML([]byte("Welcome aboard.
")) ``` -------------------------------- ### Common Assertion Helpers for Sent Emails Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/testing.md Examples of using mailertest assertion helpers to check the number of sent emails, if any were sent, if none were sent, or if email content contains specific strings. ```go mailertest.AssertEmailCount(t, rec, 2) // exactly N sent mailertest.AssertSent(t, rec) // at least one sent mailertest.AssertNotSent(t, rec) // none sent mailertest.AssertEmailContains(t, rec, "X-Audited: true") // any wire bytes contain s ``` -------------------------------- ### Wrap Middleware: Reject, Mutate, Observe Source: https://github.com/shyim/go-mailer/blob/main/docs/concepts/middleware.md Demonstrates wrapping a leaf sender with middleware to reject emails based on recipient, mutate message headers, and observe send outcomes. ```go import ( "context" "log" "github.com/shyim/go-mailer" "github.com/shyim/go-mailer/middleware" ) t := middleware.Wrap(leaf, // first argument is the OUTERMOST layer middleware.AfterSend(func(_ context.Context, sm *gomailer.SentMessage, err error) { switch { case err != nil: log.Printf("send failed: %v", err) case sm != nil: log.Printf("sent %s", sm.MessageID()) default: log.Print("send rejected") } }), middleware.BeforeSend(func(_ context.Context, msg *gomailer.Message, env *gomailer.Envelope) error { if blocked(env.Recipients()[0].Email()) { return middleware.ErrReject // skip + report success } if msg != nil { msg.SetHeader("X-Audited", "true") // mutate the send-local clone } return nil }), ) mailer := gomailer.NewMailer(t) ``` -------------------------------- ### Importing and Resolving a DSN Source: https://github.com/shyim/go-mailer/blob/main/docs/reference/dsn-options.md Blank-import transport packages to register their schemes. Then, use transport.FromDSN to resolve a DSN string into a configured transport. Ensure to pass transport.Deps{} for dependencies. ```go import ( "github.com/shyim/go-mailer/transport" _ "github.com/shyim/go-mailer/transport/sendmail" _ "github.com/shyim/go-mailer/transport/smtp" ) t, err := transport.FromDSN("smtp://user:pass@smtp.example.com:587?require_tls=true", transport.Deps{}) ``` -------------------------------- ### Handle Address Input Safely Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/sending-mail.md When dealing with user-provided email addresses, use `NewAddress` to safely parse them and handle potential errors instead of panicking. ```go from, err := gomailer.NewAddress(userInput, "") if err != nil { return err } ``` -------------------------------- ### Compose a Basic Email Message Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/sending-mail.md Use `NewMessage()` to create a message builder and chain setters to define sender, recipients, subject, and plain text body. `MustAddress` is used for convenience but panics on invalid input. ```go import "github.com/shyim/go-mailer" msg := gomailer.NewMessage(). SetFrom(gomailer.MustAddress("alice@example.com", "Alice")), SetTo(gomailer.MustAddress("bob@example.com", "Bob")), SetSubject("Hello"), SetText([]byte("Hello, Bob!")) ``` -------------------------------- ### Configure SMTP Transport Source: https://github.com/shyim/go-mailer/blob/main/docs/concepts/transports.md Sets up an SMTP transport with specified host, port, and authentication details. Supports STARTTLS and implicit TLS. Ensure to handle sensitive credentials securely. ```go import "github.com/shyim/go-mailer/transport/smtp" t := smtp.NewTransport("smtp.example.com", 587, false). // tlsOnConnect=false → STARTTLS SetUsername("user"). SetPassword("pass"). SetRequireTLS(true) ``` -------------------------------- ### Test Failover Routing with Simulated Failure Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/testing.md Demonstrates how to test failover routing by simulating a failure on the primary transport and asserting that the message is sent via the backup transport. This ensures composite routing works deterministically. ```go func TestFailoverFallsBack(t *testing.T) { primary := mailertest.NewRecordingTransport("primary") backup := mailertest.NewRecordingTransport("backup") fo, err := transport.NewFailoverTransport( []gomailer.Transport{primary, backup}, 0) if err != nil { t.Fatal(err) } primary.FailNext(errors.New("primary unavailable")) mailer := gomailer.NewMailer(fo) if err := mailer.Send(context.Background(), buildMessage(), nil); err != nil { t.Fatal(err) } mailertest.AssertNotSent(t, primary) mailertest.AssertEmailCount(t, backup, 1) } ``` -------------------------------- ### Create Failover Transport Source: https://github.com/shyim/go-mailer/blob/main/docs/concepts/transports.md Sets up a Failover transport that uses a primary transport until it fails, then switches to a backup. Includes a retry period for failed transports. ```go import ( "github.com/shyim/go-mailer" "github.com/shyim/go-mailer/transport" "github.com/shyim/go-mailer/transport/smtp" ) primary := smtp.NewTransport("a.example.com", 587, false) backup := smtp.NewTransport("b.example.com", 587, false) fo, err := transport.NewFailoverTransport( []gomailer.Transport{primary, backup}, 15*time.Second, // retryPeriod; 0 → 60s default ) if err != nil { log.Fatal(err) } mailer := gomailer.NewMailer(fo) ``` -------------------------------- ### Send Email via SMTP Source: https://github.com/shyim/go-mailer/blob/main/docs/getting-started/quickstart.md Construct an SMTP transport with credentials, create a message, and send it using the Mailer. The Envelope can be nil to derive it from the message. ```go package main import ( "context" "log" "github.com/shyim/go-mailer" "github.com/shyim/go-mailer/transport/smtp" ) func main() { // host, port, tlsOnConnect — false uses STARTTLS upgrade on a plaintext port. t := smtp.NewTransport("smtp.example.com", 587, false). SetUsername("user"). SetPassword("secret") msg := gomailer.NewMessage(). SetFrom(gomailer.MustAddress("alice@example.com", "Alice")), SetTo(gomailer.MustAddress("bob@example.com", "Bob")), SetSubject("Hello from gomailer"). SetText([]byte("Hello, Bob! (plain text)")). SetHTML([]byte("Hello, Bob! (HTML)
")) mailer := gomailer.NewMailer(t) defer mailer.Close() if err := mailer.Send(context.Background(), msg, nil); err != nil { log.Fatalf("send failed: %v", err) } } ``` -------------------------------- ### Set Cc, Bcc, and Reply-To Headers Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/sending-mail.md Add Cc, Bcc, and Reply-To recipients to the message. Each setter replaces any previously set values. ```go msg. SetCc(gomailer.MustAddress("carol@example.com", "Carol")), SetBcc(gomailer.MustAddress("audit@example.com", "")), SetReplyTo(gomailer.MustAddress("support@example.com", "Support")) ``` -------------------------------- ### Create FailoverTransport and Mailer Source: https://github.com/shyim/go-mailer/blob/main/docs/concepts/architecture.md Instantiate a FailoverTransport with primary and backup transports, then create a new Mailer instance with this transport. The second argument to NewFailoverTransport (0) indicates no initial delay before retrying. ```go fo, _ := transport.NewFailoverTransport([]gomailer.Transport{primary, backup}, 0) mailer := gomailer.NewMailer(fo) ``` -------------------------------- ### Lint and Format Code Source: https://github.com/shyim/go-mailer/blob/main/CONTRIBUTING.md Ensure code adheres to project standards by running gofmt, go vet, and golangci-lint. Use '--fix' to automatically apply formatting. ```sh gofmt -l . go vet ./... golangci-lint run ./... ``` ```sh golangci-lint run --fix ./... ``` -------------------------------- ### Create Mailer From DSN Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/dsn.md Instantiate mailer transports using `transport.FromDSN` with various supported schemes and their specific configurations. ```go transport.FromDSN("null://default", transport.Deps{}) transport.FromDSN("smtp://user:pass@mail.example.com:587", transport.Deps{}) transport.FromDSN("smtps://user:pass@mail.example.com", transport.Deps{}) transport.FromDSN("sendmail://default?command=/usr/sbin/sendmail+-t+-i", transport.Deps{}) transport.FromDSN("ses://default?region=us-east-1", transport.Deps{}) ``` -------------------------------- ### Manage Local Development Dependencies Source: https://github.com/shyim/go-mailer/blob/main/CONTRIBUTING.md Use 'go mod edit -replace' to point submodules to local core changes during development. Remember to remove the directive before committing. ```sh cd transport/smtp go mod edit -replace github.com/shyim/go-mailer=../.. # ... work, test ... go mod edit -dropreplace github.com/shyim/go-mailer # before committing ``` -------------------------------- ### Create Round-Robin Transport with FromDSN Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/dsn.md Set up a round-robin transport using FromDSN, allowing rotation through multiple DSNs for sending emails. Includes an optional retry period for dead transports. ```go transport.FromDSN( "roundrobin(smtp://user:pass@a.example.com smtp://user:pass@b.example.com)?retry_period=15", transport.Deps{}, ) ``` -------------------------------- ### Construct SES Transport with New Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/ses.md Create a new SES transport using ses.New, loading AWS configuration via the SDK's default chain and specifying options like region and configuration set. ```go import ( "context" "github.com/shyim/go-mailer" "github.com/shyim/go-mailer/transport/ses" ) func newMailer(ctx context.Context) (*gomailer.Mailer, error) { tr, err := ses.New(ctx, ses.WithRegion("us-east-1"), ses.WithConfigurationSet("my-config-set"), // optional ) if err != nil { return nil, err } return gomailer.NewMailer(tr), nil } ``` -------------------------------- ### Handling Transport Errors with errors.As Source: https://github.com/shyim/go-mailer/blob/main/docs/concepts/errors.md Demonstrates how to use errors.As to check for *TransportError and branch logic based on the SMTP response code. This is useful for implementing retry strategies for transient errors. ```go import ( "errors" "github.com/shyim/go-mailer" ) err := mailer.Send(ctx, msg, nil) var te *gomailer.TransportError if errors.As(err, &te) { switch { case te.Code >= 400 && te.Code < 500: // 4xx — temporary failure (greylisting, rate limit, server busy). // Safe to retry later. log.Printf("transient failure (%d): %s", te.Code, te.Msg) scheduleRetry(msg) case te.Code >= 500: // 5xx — permanent failure (mailbox unknown, message rejected). // Do not retry; surface to the user. log.Printf("permanent failure (%d): %s", te.Code, te.Msg) default: // Code == 0 — pre-SMTP failure (dial/TLS/DNS). Inspect te.Cause. log.Printf("connection failure: %v", te.Cause) } log.Printf("transcript:\n%s", te.Debug()) } ``` -------------------------------- ### Import OpenTelemetry Middleware Source: https://github.com/shyim/go-mailer/blob/main/docs/getting-started/installation.md Import the OpenTelemetry middleware package to bind OpenTelemetry providers to the go-mailer middleware core. This enables observability features. ```go import "github.com/shyim/go-mailer/middleware/otelmw" ``` -------------------------------- ### Initialize Null Transport Source: https://github.com/shyim/go-mailer/blob/main/docs/concepts/transports.md Creates a Null transport which accepts and discards all emails. Useful for testing or as a placeholder. ```go t := transport.NewNullTransport() ``` -------------------------------- ### Test SMTP Delivery with Fake Server Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/testing.md This Go code sets up a minimal scripted SMTP server to test the smtp.Transport. It handles basic ESMTP commands and swallows DATA. Note that AUTH requires SetAllowPlaintextAuth(true) for this cleartext server. ```go import ( "bufio" "context" "net" "strconv" "strings" "testing" smtp "github.com/shyim/go-mailer/transport/smtp" ) func TestSMTPDelivery(t *testing.T) { ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) } defer ln.Close() // Minimal scripted SMTP server: greet, accept every command, swallow DATA. go func() { conn, err := ln.Accept() if err != nil { return } defer conn.Close() r := bufio.NewReader(conn) conn.Write([]byte("220 fake ESMTP\r\n")) inData := false for { line, err := r.ReadString('\n') if err != nil { return } switch { case inData: if strings.TrimRight(line, "\r\n") == "." { inData = false conn.Write([]byte("250 OK\r\n")) } case strings.HasPrefix(line, "EHLO"): conn.Write([]byte("250-fake\r\n250 AUTH PLAIN\r\n")) case strings.HasPrefix(line, "DATA"): conn.Write([]byte("354 send data\r\n")) inData = true case strings.HasPrefix(line, "QUIT"): conn.Write([]byte("221 bye\r\n")) return default: // MAIL, RCPT, ... conn.Write([]byte("250 OK\r\n")) } } }() host, portStr, _ := net.SplitHostPort(ln.Addr().String()) port, _ := strconv.Atoi(portStr) tr := smtp.NewTransport(host, port, false). SetAllowPlaintextAuth(true) // only for a trusted, in-test relay defer tr.Close() sm, err := tr.Send(context.Background(), buildMessage(), nil) if err != nil { t.Fatalf("send failed: %v", err) } if sm.MessageID() == "" { t.Fatal("expected a Message-ID after a successful send") } } ``` -------------------------------- ### Custom TLS Configuration Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/production.md Set a custom TLS configuration, including minimum TLS version and a custom root certificate pool, for advanced control over the secure connection. ```go t := smtp.NewTransport("smtp.example.com", 587, false). SetTLSConfig(&tls.Config{ MinVersion: tls.VersionTLS12, RootCAs: pool, }) ``` -------------------------------- ### Using Recording Transport for Testing Source: https://github.com/shyim/go-mailer/blob/main/docs/concepts/transports.md Utilize the recording transport for tests to capture all messages in memory. Use FailNext to simulate send failures and test failover logic. ```go import "github.com/shyim/go-mailer/mailertest" rec := mailertest.NewRecordingTransport("") rec.FailNext(gomailer.NewTransportError("boom")) // force the next Send to fail ``` -------------------------------- ### Create Failover Transport with FromDSN Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/dsn.md Configure a failover transport using FromDSN to specify a primary DSN and a backup DSN. The system attempts the next DSN only when the current one fails. ```go transport.FromDSN( "failover(smtp://user:pass@a.example.com smtp://user:pass@b.example.com)", transport.Deps{}, ) ``` -------------------------------- ### Wrap Leaf Transport with OTel Middleware Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/observability.md Instantiate the OpenTelemetry middleware and wrap individual leaf transports. This ensures that each delivery attempt, including retries, is captured with its own span and metric. ```go import ( "github.com/shyim/go-mailer" "github.com/shyim/go-mailer/middleware" "github.com/shyim/go-mailer/middleware/otelmw" "github.com/shyim/go-mailer/transport" ) mw := otelmw.New(tracerProvider, meterProvider) // a middleware.Middleware // Single transport: t := middleware.Wrap(leaf, mw) // Composites — wrap each LEAF, not the composite, so every attempt is its own // span. Use Chain to reuse the same stack: stack := middleware.Chain(mw) fo, _ := transport.NewFailoverTransport([]gomailer.Transport{ stack(primaryLeaf), stack(backupLeaf), }, 0) ``` -------------------------------- ### Transport Decorators and Composites Source: https://github.com/shyim/go-mailer/blob/main/docs/concepts/architecture.md Shows how middleware decorators and composite transports like RoundRobinTransport, FailoverTransport, and Transports can be used to enhance or route messages. ```go middleware.Wrap(...) ``` ```go (transparent: String() delegates to the wrapped transport) ``` ```go RoundRobinTransport ``` ```go FailoverTransport ``` ```go Transports ``` -------------------------------- ### Construct SES Transport with Static Credentials Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/ses.md Configure the SES transport with static AWS credentials by providing access key ID, secret access key, and an optional session token. ```go tr, err := ses.New(ctx, ses.WithRegion("us-east-1"), ses.WithCredentials(accessKeyID, secretAccessKey, ""), // sessionToken optional ) ``` -------------------------------- ### Run Locally with Test Transport Source: https://github.com/shyim/go-mailer/blob/main/docs/getting-started/quickstart.md Uses mailertest.RecordingTransport to capture messages in memory without a real SMTP server. Useful for local development and testing. ```go package main import ( "context" "fmt" "log" "github.com/shyim/go-mailer" "github.com/shyim/go-mailer/mailertest" ) func main() { rec := mailertest.NewRecordingTransport("") mailer := gomailer.NewMailer(rec) msg := gomailer.NewMessage(). SetFrom(gomailer.MustAddress("alice@example.com", "Alice")), SetTo(gomailer.MustAddress("bob@example.com", "Bob")), SetSubject("Welcome"), SetText([]byte("hi")) if err := mailer.Send(context.Background(), msg, nil); err != nil { log.Fatal(err) } fmt.Println("captured:", rec.Count()) // 1 if sent, ok := rec.Last(); ok { fmt.Println(string(sent.Bytes())) // the serialized MIME message } } ``` -------------------------------- ### Testing Queued Email Count (Always Zero) Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/testing.md Demonstrates the usage of AssertQueuedEmailCount, which only passes for a count of 0 because go-mailer sends emails synchronously. ```go mailertest.AssertQueuedEmailCount(t, rec, 0) // OK mailertest.AssertQueuedEmailCount(t, rec, 1) // always fails ``` -------------------------------- ### Configure Mailer from DSN Source: https://github.com/shyim/go-mailer/blob/main/docs/getting-started/quickstart.md Resolves a transport from a DSN string. Requires blank-importing concrete transport packages like smtp. Registers smtp:// and smtps:// schemes. ```go package main import ( "context" "log" "github.com/shyim/go-mailer" "github.com/shyim/go-mailer/transport" _ "github.com/shyim/go-mailer/transport/smtp" // registers smtp:// and smtps:// ) func main() { t, err := transport.FromDSN( "smtp://user:pass@smtp.example.com:587?require_tls=true", transport.Deps{}, ) if err != nil { log.Fatal(err) } msg := gomailer.NewMessage(). SetFrom(gomailer.MustAddress("alice@example.com", "Alice")), SetTo(gomailer.MustAddress("bob@example.com", "Bob")), SetSubject("Hello via DSN"), SetText([]byte("Configured from a DSN string.")) mailer := gomailer.NewMailer(t) defer mailer.Close() if err := mailer.Send(context.Background(), msg, nil); err != nil { log.Fatalf("send failed: %v", err) } } ``` -------------------------------- ### Run All Tests Source: https://github.com/shyim/go-mailer/blob/main/CONTRIBUTING.md Execute tests for all modules independently. This ensures each component functions correctly in isolation. ```sh go test ./... # core (cd transport/smtp && go test ./...) (cd transport/sendmail && go test ./...) (cd middleware/otelmw && go test ./...) ``` -------------------------------- ### Import Root Module Source: https://github.com/shyim/go-mailer/blob/main/docs/getting-started/installation.md Import the root go-mailer package to access its functionalities. This import does not include OpenTelemetry or third-party SMTP clients. ```go import "github.com/shyim/go-mailer" ``` -------------------------------- ### Leaf Transports Source: https://github.com/shyim/go-mailer/blob/main/docs/concepts/architecture.md Lists the concrete leaf transport implementations: smtp.Transport for ESMTP, sendmail.Transport for local delivery, NullTransport for discarding, and RecordingTransport for testing. ```go smtp.Transport ESMTP over net.Conn ``` ```go sendmail.Transport pipe raw MIME to a local binary ``` ```go NullTransport discards everything ``` ```go RecordingTransport in-memory capture (mailertest) ``` -------------------------------- ### Run All Tests Source: https://github.com/shyim/go-mailer/blob/main/README.md Execute all tests within the project modules. This command runs tests for the root module and then specifically for the 'transport/smtp' module. ```sh go test ./... ``` ```sh (cd transport/smtp && go test ./...) ``` -------------------------------- ### Create Transports Router Source: https://github.com/shyim/go-mailer/blob/main/docs/concepts/transports.md Instantiate a transports router, specifying a map of transport names to transport implementations and an ordered list of transport names to define the default. ```go router, err := transport.NewTransports(map[string]gomailer.Transport{ "main": primary, "backup": backup, }, []string{"main", "backup"}) // order; first → default ``` -------------------------------- ### Configure Sendmail Transport Source: https://github.com/shyim/go-mailer/blob/main/docs/concepts/transports.md Initializes a Sendmail transport that pipes email data to a local sendmail-compatible binary. The default command is /usr/sbin/sendmail -t -i. ```go import "github.com/shyim/go-mailer/transport/sendmail" t, err := sendmail.NewSendmailTransport("/usr/sbin/sendmail -t -i") ``` -------------------------------- ### Importing and Parsing a Transport DSN Source: https://github.com/shyim/go-mailer/blob/main/docs/reference/index.md Import necessary transport packages and parse a DSN string into a Transport object. Ensure to blank-import the specific transport modules you intend to use. ```go import ( "github.com/shyim/go-mailer/transport" _ "github.com/shyim/go-mailer/transport/smtp" _ "github.com/shyim/go-mailer/transport/sendmail" ) t, err := transport.FromDSN("smtp://user:pass@smtp.example.com:587?require_tls=true", transport.Deps{}) ``` -------------------------------- ### Import Sendmail Transport Source: https://github.com/shyim/go-mailer/blob/main/docs/getting-started/installation.md Import the Sendmail transport package to enable sending emails via the local sendmail executable. This is necessary for its integration. ```go import "github.com/shyim/go-mailer/transport/sendmail" ``` -------------------------------- ### Chain Middleware for Reuse Source: https://github.com/shyim/go-mailer/blob/main/docs/concepts/middleware.md Composes a list of middlewares into a single `Middleware` that can be applied to multiple transports. ```go stack := middleware.Chain(A, B, C) t1 := stack(leaf1) t2 := stack(leaf2) ``` -------------------------------- ### Failover DSN with Retry Period in Go Source: https://github.com/shyim/go-mailer/blob/main/docs/reference/dsn-options.md Initializes a failover transport from a DSN string in Go. The retry_period is specified in seconds. ```go t, err := transport.FromDSN( "failover(smtp://user:pass@primary.example.com smtp://user:pass@backup.example.com)?retry_period=60", transport.Deps{}, ) ``` -------------------------------- ### Attaching a Regular File Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/sending-mail.md Attaches a file to an email. The content type is automatically determined from the filename extension. ```go msg.Attach(gomailer.Attachment{ Filename: "invoice.pdf", Data: pdfBytes, // []byte }) ``` -------------------------------- ### Blank Import Go Mailer Transports Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/dsn.md Blank import transport packages to register their schemes. The `null://` scheme is always available; others require explicit imports. ```go import ( "github.com/shyim/go-mailer/transport" _ "github.com/shyim/go-mailer/transport/sendmail" // sendmail:// _ "github.com/shyim/go-mailer/transport/smtp" // smtp://, smtps:// ) ``` -------------------------------- ### Composite DSN with Retry Period Source: https://github.com/shyim/go-mailer/blob/main/docs/reference/dsn-options.md Shows how to set a retry period for composite DSNs, specifying how long a failed child DSN remains marked dead before being retried. ```text roundrobin(smtp://a.example.com smtp://b.example.com)?retry_period=15 ``` -------------------------------- ### Wrap Middleware with Transport Source: https://github.com/shyim/go-mailer/blob/main/docs/concepts/middleware.md Applies a list of middlewares to a leaf transport. The first middleware listed is the outermost layer. ```go import "github.com/shyim/go-mailer/middleware" // request flows A -> B -> C -> leaf t := middleware.Wrap(leaf, A, B, C) ``` -------------------------------- ### Test Sending and Inspecting an HTML Email Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/testing.md This snippet demonstrates how to send an HTML email using gomailer and then inspect the recorded message to verify its content and recipient. ```go func TestRendersHTMLBody(t *testing.T) { rec := mailertest.NewRecordingTransport("") mailer := gomailer.NewMailer(rec) msg := gomailer.NewMessage(). SetFrom(gomailer.MustAddress("alice@example.com", "")). SetTo(gomailer.MustAddress("bob@example.com", "")), SetSubject("Receipt"). SetHTML([]byte("Thanks!
")) if err := mailer.Send(context.Background(), msg, nil); err != nil { t.Fatal(err) } sm, ok := rec.Last() if !ok { t.Fatal("no message recorded") } if !bytes.Contains(sm.Bytes(), []byte("text/html")) { t.Fatalf("expected an HTML part, got:\n%s", sm.Bytes()) } env := sm.Envelope() if got := env.Recipients()[0].Email(); got != "bob@example.com" { t.Fatalf("unexpected recipient: %s", got) } } ``` -------------------------------- ### Configure Named Transports with FromDSNs Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/dsn.md Use FromDSNs to create a transport router from a map of named DSNs. Messages are dispatched based on the X-Transport header or default to the first transport. ```go router, err := transport.FromDSNs(map[string]string{ "main": "smtp://user:pass@a.example.com", "backup": "sendmail://default", }, transport.Deps{}) if err != nil { log.Fatal(err) } mailer := gomailer.NewMailer(router) ``` -------------------------------- ### Send Email with Context Timeout Source: https://github.com/shyim/go-mailer/blob/main/docs/guides/sending-mail.md Use `context.WithTimeout` to set a deadline for the `mailer.Send` operation. Ensure `cancel()` is deferred to release resources. This is useful for bounding slow relay operations. ```go ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := mailer.Send(ctx, msg, nil); err != nil { log.Printf("send failed: %v", err) } ``` -------------------------------- ### Build Transport Router from DSNs Source: https://github.com/shyim/go-mailer/blob/main/docs/reference/dsn-options.md Constructs a transport router from a map of transport names to their corresponding DSNs. This router can then be used to dispatch emails based on the 'X-Transport' header. ```go router, err := transport.FromDSNs(map[string]string{ "main": "smtp://user:pass@a.example.com", "backup": "sendmail://default", }, transport.Deps{}) ``` -------------------------------- ### Local Replace Directive for Development Source: https://github.com/shyim/go-mailer/blob/main/docs/getting-started/installation.md Configure local 'replace' directives in your go.mod file when working against a local checkout of the repository instead of a tagged release. This ensures submodules resolve to your local copy. ```toml require github.com/shyim/go-mailer v0.0.0 replace github.com/shyim/go-mailer => ../path/to/gomailer ```