mdsend

package module
v0.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: MIT Imports: 31 Imported by: 0

README

Mdsend

Go Reference

Send markdown files as electronic mail.

Features

  • Durable: mail queues are fault tolerant and atomic, brokered by <Watermill.io> over SQLite3. Can handle any volume of mail without degredation.
  • Flexible: select the mailer backend based on highest deliverability. Swap it out later without changing anything in your letters or templates.
    • Supports recipient list formats: CSV, JSON, YAML, TOML, and Cue.
    • Supports mailing services:
  • Portable: runs on many kinds of systems. Will include an embeddable HTTP service and Posgres support in the future.

Examples

Compose a letter as Markdown file. It must include the subject, the sender, and at least one recipient in the frontmatter:

---
subject: "Test Email"
from: "Sender <test@gmail.com>"
to: "Recipient <test@gmail.com>"
---

# Title of the Example Letter

Write text in **Markdown** notation.

Provide server credentials and point mdsend at the saved file.

export SMTP_HOST=...
export SMTP_PORT=...
export SMTP_USERNAME=...
export SMTP_PASSWORD=...
mdsend send letter.md

Annotated examples are the documentation:

Installation

  • MacOS:
    brew tap dkotik/tap
    brew install curl mdsend
    
  • Build from source:
    go install github.com/dkotik/mdsend@latest
    

Development

Mdsend is under active development. Version 1.0.0 is expected by the end of 2026.

Click here to see a list of planned features.

E-mail Tools

Similar Projects

Documentation

Index

Constants

View Source
const (
	FieldNameID                        = "id"
	FieldNameSeed                      = "seed"
	FieldNameExtends                   = "extends"
	FieldNameDatabase                  = "queue"
	FieldNameSubject                   = "subject"
	FieldNameReplyTo                   = "reply_to"
	FieldNameAttachments               = "attachments"
	FieldNameAttachmentName            = "name"
	FieldNameAttachmentLocation        = "location"
	FieldNameTemplates                 = "templates"
	FieldNameHeaders                   = "headers"
	FieldNameLanguage                  = "language"
	FieldNameMediaContraints           = "media_constraints"
	FieldNameMediaConstraintsQuality   = "quality"
	FieldNameMediaConstrainsResolution = "resolution"
	FieldNameMediaConstraintsWidth     = "width"
	FieldNameMediaConstrainsHeight     = "height"
	FieldNameSchedule                  = "schedule"
	FieldNameScheduleAfter             = "after"
	FieldNameScheduleDelay             = "delay"
	FieldNameScheduleStep              = "step"
	FieldNameScheduleExpire            = "expire"
	FieldNameScheduleFluctuate         = "fluctuate"
)
View Source
const Version = "dev"

Variables

This section is empty.

Functions

func EachAttachmentSourceRelativeWithPathPrefix added in v0.0.5

func EachAttachmentSourceRelativeWithPathPrefix(prefix string, each iter.Seq2[AttachmentSource, error]) iter.Seq2[AttachmentSource, error]

Types

type Attachment

type Attachment struct {
	LetterID string
	Name     string
	Source   string
	// Hash                     uint64 // for XXHash2
	Hash string

	// ContentID is the ID of the inline attachment to use in the message.
	// It must conform to RFC 2392 format, including the angle brackets:
	// <hash@domain.com>. Leave empty for standalone attachments that
	// are not referenced by other parts of the message.
	ContentID   string
	ContentType string
	Content     []byte
}

func NewAttachment

func NewAttachment(b []byte, constraints media.Constraints) (a Attachment, err error)

func NewAttachmentFromFile added in v0.0.5

func NewAttachmentFromFile(fs fs.FS, p string, constraints media.Constraints) (a Attachment, err error)

func (Attachment) AssertEqualityTo added in v0.0.5

func (a Attachment) AssertEqualityTo(b Attachment) error

func (Attachment) IsEqualTo added in v0.0.5

func (a Attachment) IsEqualTo(b Attachment) bool

func (Attachment) LogValue added in v0.0.5

func (a Attachment) LogValue() slog.Value

func (Attachment) Validate added in v0.0.5

func (a Attachment) Validate() (err error)

func (Attachment) WithUpdatedHash added in v0.0.5

func (a Attachment) WithUpdatedHash() Attachment

type AttachmentError added in v0.0.5

type AttachmentError uint8
const (
	ErrInvalidAttachment AttachmentError = iota
	ErrDuplicateAttachment
)

func (AttachmentError) Error added in v0.0.5

func (err AttachmentError) Error() string

type AttachmentReference added in v0.0.5

type AttachmentReference struct {
	ID          string
	Name        string
	ContentID   string
	ContentType string
}

type AttachmentSource added in v0.0.5

type AttachmentSource struct {
	Name     string
	Location string
}

type FieldComparisonMismatchError added in v0.0.5

type FieldComparisonMismatchError struct {
	FieldName     string
	ExpectedValue any
	ActualValue   any
}

func (FieldComparisonMismatchError) Error added in v0.0.5

type Letter added in v0.0.5

type Letter struct {
	ID string
	// Frontmatter orderedmap.OrderedMap[string, any]
	Frontmatter map[string]any
	Templates   []Attachment
	Content     string
	CreatedAt   time.Time
	SentAt      time.Time
}

func NewLetter added in v0.0.5

func NewLetter(b []byte) (letter Letter, err error)

func NewLetterFromFile added in v0.0.5

func NewLetterFromFile(
	ctx context.Context,
	fs fs.FS,
	p string,
) (letter Letter, err error)

func (Letter) AssertEqualityTo added in v0.0.5

func (l Letter) AssertEqualityTo(b Letter) error

func (Letter) EachAttachmentSource added in v0.0.5

func (l Letter) EachAttachmentSource() iter.Seq2[AttachmentSource, error]

func (Letter) EachWarning added in v0.0.5

func (l Letter) EachWarning(fs fs.FS) iter.Seq2[*i18n.LocalizeConfig, error]

func (Letter) GetDatabase added in v0.0.5

func (l Letter) GetDatabase() string

func (Letter) GetExpiration added in v0.1.0

func (l Letter) GetExpiration() (t time.Duration, err error)

func (Letter) GetFrom added in v0.0.5

func (l Letter) GetFrom() (mail.Address, error)

GetFrom returns the address.FieldFrom address from the frontmatter. There must be only one valid address. Mutliple from addresses can disrupt delivery.

func (Letter) GetHeaders added in v0.0.5

func (l Letter) GetHeaders() (headers []header.Header, err error)

func (Letter) GetLanguage added in v0.0.5

func (l Letter) GetLanguage() (lang language.Tag, err error)

func (Letter) GetMediaConstraints added in v0.0.5

func (l Letter) GetMediaConstraints() (m media.Constraints, err error)

func (Letter) GetSchedule added in v0.0.5

func (l Letter) GetSchedule() (s Schedule, err error)

func (Letter) GetSeed added in v0.0.5

func (l Letter) GetSeed() (string, error)

func (Letter) GetSubject added in v0.0.5

func (l Letter) GetSubject() (string, error)

func (Letter) IsEqualTo added in v0.0.5

func (l Letter) IsEqualTo(b Letter) bool

func (Letter) IsValid added in v0.0.5

func (l Letter) IsValid(cxt context.Context, logger *slog.Logger) (ok bool)

func (Letter) LogValue added in v0.0.5

func (l Letter) LogValue() slog.Value

func (Letter) Validate added in v0.0.5

func (l Letter) Validate() (err error)

type LetterError added in v0.0.5

type LetterError uint
const (
	ErrNoSubject LetterError = iota + 1
	// ErrNoQueue
	ErrDuplicateLetter
	ErrNoFromAddress
	ErrNoContent
	ErrNotFound
	ErrFieldNotFound
)

func (LetterError) Error added in v0.0.5

func (l LetterError) Error() string

type Mailer

type Mailer interface {
	SendMail(context.Context, Message) (string, error)
}

type Message

type Message struct {
	ID            string
	LetterID      string
	SeedKey       string
	Headers       []header.Header
	From          mail.Address
	To            mail.Address
	Subject       string
	Text          string
	HTML          string
	ScheduleAfter time.Time
	ScheduledAt   time.Time
	SentAt        time.Time
}

Message is an intent to delivery a copy of a letter to a particular recipient.

func (Message) AssertEqualityTo added in v0.0.5

func (m Message) AssertEqualityTo(b Message) error

func (Message) IsEqualTo added in v0.0.5

func (m Message) IsEqualTo(b Message) bool

func (Message) LogValue added in v0.0.5

func (m Message) LogValue() slog.Value

func (Message) Validate added in v0.0.5

func (m Message) Validate() (err error)

type MessageError added in v0.0.5

type MessageError uint8
const (
	ErrInvalidMessage MessageError = iota
	ErrDuplicateMessage
	ErrMessageNotFound
)

func (MessageError) Error added in v0.0.5

func (err MessageError) Error() string

type Schedule added in v0.0.5

type Schedule struct {
	After     time.Time     `json:"after"`
	Step      time.Duration `json:"step"`
	Fluctuate time.Duration `json:"fluctuate"`
	Expire    time.Duration `json:"expire"`
}

func (Schedule) Next added in v0.0.5

func (s Schedule) Next() Schedule

Directories

Path Synopsis
cmd
mdsend command
mime
Package mime encodes electronic mail parts for delivery.
Package mime encodes electronic mail parts for delivery.
smtp
Package smtp sends MIME-formatted emails via SMTP.
Package smtp sends MIME-formatted emails via SMTP.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL