mailer

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Oct 7, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package mailer sends the app's mail and reads its mailbox. The app sends from its one address, email.from in the permission list, and only to the domains in email.to_domains; the runner enforces both:

email:
  from: ops@acme.example
  to_domains: [acme.example]

Rules for Send:

  • Every Message has an IdempotencyKey that names what the mail is about, such as invoice-42-reminder. Sending the same key again sends nothing and returns the first message's id, so a retried request never mails twice.
  • The domain of every recipient, and of ReplyTo unless it is the app's own address, equals one of email.to_domains (E-MAIL-003).
  • Headers may set only In-Reply-To, References, List-Id, List-Unsubscribe, List-Unsubscribe-Post, Auto-Submitted and Precedence.
  • Send after the data the mail reports is committed, never before.

The app's mailbox has the folders Inbox, Archive and Trash. List reads a page of a folder, newest first, Get reads a message, Move moves it to another folder and Delete removes it. Without an email section in the permission list, every call fails with E-MAN-012. In aicoded dev, mail is caught and shown on the dev UI, never sent.

Read more in the guide docs/guides/mail.md and the task docs/tasks/email-after-a-write.md, which aicoded explain and the MCP tool howto print as guides/mail and tasks/email-after-a-write.

Index

Examples

Constants

View Source
const (
	Inbox   = "inbox"
	Archive = "archive"
	Trash   = "trash"
)

Folders of the app's mailbox.

Variables

View Source
var ErrNotFound = errors.New("mailer: no such message")

ErrNotFound is returned for a message id the mailbox does not hold.

Functions

func Delete

func Delete(ctx context.Context, id string) error

Delete removes the message id from the mailbox, or returns ErrNotFound.

func Move

func Move(ctx context.Context, id, folder string) error

Move moves the message id to folder, or returns ErrNotFound.

func Send

func Send(ctx context.Context, msg Message) (string, error)

Send checks msg against the mail rules and hands it to the runner, and returns its id.

Example

Send a mail after the data it reports is committed, here in a form's Process hook. The idempotency key names what the mail is about, so a retried request sends it once.

package main

import (
	"context"

	"aicoded.dev/framework/mailer"
)

func main() {
	process := func(ctx context.Context) error {
		_, err := mailer.Send(ctx, mailer.Message{
			IdempotencyKey: "invoice-42-reminder",
			To:             []mailer.Address{{Name: "Accounts", Address: "accounts@acme.example"}},
			Subject:        "Invoice 42 is due tomorrow",
			Text:           "Invoice 42 is due tomorrow. Its details are in the app.",
		})
		return err
	}
	_ = process
}

Types

type Address

type Address struct{ Name, Address string }

Address is one mailbox: a plain address and the name shown with it.

type Attachment

type Attachment struct {
	Filename, ContentType string
	Data                  []byte
}

Attachment is a file sent with or received in a message.

type Header struct{ Name, Value string }

Header is one extra header field.

type Mail

type Mail struct {
	Summary
	Cc          []Address
	ReplyTo     *Address
	Text, HTML  string
	Attachments []Attachment
	Headers     []Header
}

Mail is a whole message from the mailbox.

func Get

func Get(ctx context.Context, id string) (*Mail, error)

Get returns the message id, or ErrNotFound.

type Message

type Message struct {
	IdempotencyKey string
	FromName       string
	To, Cc, Bcc    []Address
	ReplyTo        *Address
	Subject        string
	Text, HTML     string
	Attachments    []Attachment
	// Headers may set only In-Reply-To, References, List-Id, List-Unsubscribe,
	// List-Unsubscribe-Post, Auto-Submitted and Precedence.
	Headers []Header
}

Message is one outbound email. The sender is always the app's address; FromName sets the name shown with it. IdempotencyKey is required: sending the same key again returns the first message's id and sends nothing, so a retried request never mails twice.

type Summary

type Summary struct {
	ID      string
	From    Address
	To      []Address
	Subject string
	Date    time.Time
	Size    int64
	Folder  string
}

Summary describes a message in the mailbox.

func List

func List(ctx context.Context, folder string, offset, limit int) ([]Summary, int, error)

List returns a page of folder, newest first, and the number of messages in the folder. The page skips offset messages and holds up to limit, and never more than 100. A negative offset counts as 0, and a limit of 0 or less as 50.

Example

List reads a page of a folder of the app's mailbox, newest first, here in a page's Data.

package main

import (
	"context"
	"fmt"
	"time"

	"aicoded.dev/framework/mailer"
)

func main() {
	data := func(ctx context.Context) error {
		page, total, err := mailer.List(ctx, mailer.Inbox, 0, 20)
		if err != nil {
			return err
		}
		fmt.Printf("%d of %d messages\n", len(page), total)
		for _, m := range page {
			fmt.Println(m.Date.Format(time.DateOnly), m.Subject)
		}
		return nil
	}
	_ = data
}

Jump to

Keyboard shortcuts

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