Documentation
¶
Overview ¶
auth.go - Authentication helpers for OAuth2 providers
calendar.go - provider-agnostic calendar types and the Client wrappers that expose them. The Outlook implementation lives in outlook_calendar.go; Gmail does not implement CalendarProvider (the build-in gmailProvider returns ErrUnsupported via the type assertion in calendar()).
config.go - Configuration helpers for the email package
Package email provides a simple, provider-agnostic interface for sending emails through various providers including Outlook 365 and Gmail.
Basic usage:
config := &email.Config{
Provider: "outlook365",
Outlook: &email.OutlookConfig{
TenantID: "your-tenant-id",
ClientID: "your-client-id",
ClientSecret: "your-client-secret",
},
}
client, err := email.NewClient(config)
if err != nil {
log.Fatal(err)
}
msg := &email.Message{
From: "sender@company.com",
To: []string{"recipient@example.com"},
Subject: "Hello",
Body: "This is a test email!",
}
if err := client.Send(msg); err != nil {
log.Fatal(err)
}
errors.go - Sentinel errors for the email package.
gmail.go - Gmail provider implementation using Gmail API
gmail_read.go - Gmail implementation of the MailboxProvider read/management operations. The send path lives in gmail.go; this file adds list/read/ search/move/attachments/flags/delete/folders.
Gmail differences baked in here, per the verified API reference:
- No folders, only labels. "Folder" == label; Move == relabel.
- Messages.List returns only {Id, ThreadId} stubs; each needs a Get.
- Bodies and attachments are base64URL WITHOUT padding -> RawURLEncoding.
- Modify/Trash/Labels need gmail.modify scope; permanent Delete needs the full mail.google.com scope.
mailbox.go - Read/manage operations (list, read, search, move, labels, attachments, delete, folders) layered additively on top of the send-only core. Existing send-only callers are unaffected: they simply never call these methods. Both the Outlook 365 and Gmail providers implement them.
Provider-portability note: Outlook has real folders; Gmail has only labels. The interface speaks a common vocabulary of folder/label NAMES (e.g. "Inbox", "Archive"); each provider maps names to its own identifiers. Gmail "Move" is implemented as a label change (add destination, remove INBOX). These quirks are documented per method rather than leaked into the type system.
outlook.go - Outlook 365 provider implementation using Microsoft Graph API
outlook_calendar.go - Outlook 365 (Microsoft Graph) implementation of the CalendarProvider interface. Mirrors the SDK idiom of outlook_read.go: the configured UserID is the mailbox, builder/config type names are verified against msgraph-sdk-go v1.59.0, and @odata.nextLink is followed for list.
Graph permission required: Calendars.ReadWrite (application) on the same Azure app dl/go-email already use. With only Mail.* the event calls 403.
outlook_read.go - Outlook 365 (Microsoft Graph) implementation of the MailboxProvider read/management operations. The send path lives in outlook.go; this file adds list/read/search/move/attachments/flags/delete/ folders. All builder and query-parameter type names here were verified against the generated msgraph-sdk-go source (stable across v1.59.0 the standalone pin and the workspace's newer pin).
outlook_retry.go - Idempotency-aware retry policy for the Microsoft Graph client (bd dl-jbb).
WHY THIS EXISTS --------------- The kiota default RetryHandler retries HTTP 429/503/504 for ANY method, including a POST, as long as the request body has a known Content-Length (which a sendMail POST always does). A 504 Gateway Timeout is AMBIGUOUS: Graph may already have accepted and queued the message before the gateway timed out. Retrying a sendMail POST on a 504 therefore risks DELIVERING A DUPLICATE email with no error surfaced to the caller. See the audit at outlook-tools/owner-inbox/gus-go-email-retry-audit-2026-07-03.md.
This file installs a RetryHandler whose ShouldRetry mirrors the hardened classification in outlook-tools' Python graph_client.py:
- 429 / 503 -> retried for ANY method (server signalled "not processed", so a retry cannot duplicate a side effect).
- 500 / 502 / 504 -> AMBIGUOUS; retried ONLY for idempotent methods (GET/HEAD/OPTIONS). Never retried for POST/PATCH/PUT/ DELETE — this closes the sendMail double-send hole and equally protects calendar create/update/delete and message move (all POST/PATCH/DELETE on the shared client).
- everything else -> handled by the default retriable-status set (isRetriableErrorCode already limits retries to 429/503/504, so 500/502 are not retried at all by the handler; our predicate only ever narrows, never widens).
The whole Outlook surface (mail send, mail read, calendar, move) shares one *GraphServiceClient built in newOutlookProvider, so wiring the policy at the client-construction seam covers every Graph call in one place.
sanitize.go - Filesystem-safety helpers shared by the provider .eml/raw-MIME filing paths. Pure functions, no network, no provider knowledge: they turn a message subject into a single safe path element with a guaranteed ".eml" suffix, so the consumer (e.g. dl) can pass a raw subject and the library owns "make this a safe .eml name". Sits beside writeUniqueAttachment in spirit; both providers reuse it.
Package email version information
Index ¶
- Constants
- Variables
- func AuthenticateGmailFromFile(credentialsFile string) ([]byte, error)
- func GetVersion() string
- func QuickSend(provider string, creds interface{}, from, to, subject, body string) error
- type Attachment
- type AttachmentMeta
- type CalendarProvider
- type Client
- func (c *Client) AddAttachment(id string, att Attachment) error
- func (c *Client) AddAttachmentWithContext(ctx context.Context, id string, att Attachment) error
- func (c *Client) CreateEvent(e Event) (*Event, error)
- func (c *Client) CreateEventWithContext(ctx context.Context, e Event) (*Event, error)
- func (c *Client) CreateMasterCategory(name, color string) error
- func (c *Client) CreateMasterCategoryWithContext(ctx context.Context, name, color string) error
- func (c *Client) CreateReplyDraft(id, htmlBody string, replyAll bool) (*DraftInfo, error)
- func (c *Client) CreateReplyDraftWithContext(ctx context.Context, id, htmlBody string, replyAll bool) (*DraftInfo, error)
- func (c *Client) Delete(id string, permanent bool) error
- func (c *Client) DeleteEvent(id string) error
- func (c *Client) DeleteEventWithContext(ctx context.Context, id string) error
- func (c *Client) DeleteWithContext(ctx context.Context, id string, permanent bool) error
- func (c *Client) List(opts ListOptions) ([]Summary, error)
- func (c *Client) ListAttachments(id string) ([]AttachmentMeta, error)
- func (c *Client) ListAttachmentsWithContext(ctx context.Context, id string) ([]AttachmentMeta, error)
- func (c *Client) ListEvents(opts EventListOptions) ([]Event, error)
- func (c *Client) ListEventsWithContext(ctx context.Context, opts EventListOptions) ([]Event, error)
- func (c *Client) ListFolders() ([]Folder, error)
- func (c *Client) ListFoldersWithContext(ctx context.Context) ([]Folder, error)
- func (c *Client) ListMasterCategories() ([]MasterCategory, error)
- func (c *Client) ListMasterCategoriesWithContext(ctx context.Context) ([]MasterCategory, error)
- func (c *Client) ListWithContext(ctx context.Context, opts ListOptions) ([]Summary, error)
- func (c *Client) MarkRead(id string, read bool) error
- func (c *Client) MarkReadWithContext(ctx context.Context, id string, read bool) error
- func (c *Client) Move(id, dest string) error
- func (c *Client) MoveWithContext(ctx context.Context, id, dest string) error
- func (c *Client) Read(id string) (*FullMessage, error)
- func (c *Client) ReadEvent(id string) (*Event, error)
- func (c *Client) ReadEventWithContext(ctx context.Context, id string) (*Event, error)
- func (c *Client) ReadWithContext(ctx context.Context, id string) (*FullMessage, error)
- func (c *Client) SaveAttachments(id, destDir string) ([]string, error)
- func (c *Client) SaveAttachmentsWithContext(ctx context.Context, id, destDir string) ([]string, error)
- func (c *Client) SaveMessageRaw(id, destDir, baseName string) (string, error)
- func (c *Client) SaveMessageRawWithContext(ctx context.Context, id, destDir, baseName string) (string, error)
- func (c *Client) Search(query string, opts ListOptions) ([]Summary, error)
- func (c *Client) SearchWithContext(ctx context.Context, query string, opts ListOptions) ([]Summary, error)
- func (c *Client) Send(msg *Message) error
- func (c *Client) SendWithContext(ctx context.Context, msg *Message) error
- func (c *Client) SetLabels(id string, labels []string) error
- func (c *Client) SetLabelsWithContext(ctx context.Context, id string, labels []string) error
- func (c *Client) UpdateEvent(id string, e Event) (*Event, error)
- func (c *Client) UpdateEventWithContext(ctx context.Context, id string, e Event) (*Event, error)
- type Config
- type DraftInfo
- type Event
- type EventListOptions
- type Folder
- type FullMessage
- type GmailAuthHelper
- type GmailConfig
- type ListOptions
- type MailboxProvider
- type MasterCategory
- type Message
- type OutlookConfig
- type Provider
- type Summary
- type VersionInfo
Constants ¶
const ( ProviderOutlook365 = "outlook365" ProviderGmail = "gmail" )
Provider name constants, used as the Config.Provider value and the EMAIL_PROVIDER env var.
const ( // Version is the current version of the go-email package Version = "v1.5.1" // VersionMajor is the major version number VersionMajor = 1 // VersionMinor is the minor version number VersionMinor = 5 // VersionPatch is the patch version number VersionPatch = 1 // VersionPreRelease is the pre-release version identifier VersionPreRelease = "" // BuildDate is the date the binary was built (set during build) BuildDate = "" // GitCommit is the git commit hash (set during build) GitCommit = "" )
Version information
const MaxDirectAttachmentBytes = 3 * 1024 * 1024
MaxDirectAttachmentBytes is the ceiling for a single DIRECT attachment upload (a whole-content POST to /messages/{id}/attachments). Microsoft Graph accepts a fileAttachment posted inline this way only up to 3 MB; a larger file requires a createUploadSession chunked upload, which AddAttachment does NOT implement. We reject over-size content up front with a clear error rather than let Graph fail mid-upload with a less actionable 413/400.
Variables ¶
var ( // ErrUnsupported is returned when a configured provider does not implement // the requested mailbox operation (i.e. it is not a MailboxProvider). ErrUnsupported = errors.New("operation not supported by provider") // ErrNotFound is returned when a referenced message, folder, or label does // not exist. ErrNotFound = errors.New("not found") )
Functions ¶
func AuthenticateGmailFromFile ¶
AuthenticateGmailFromFile is a convenience function that reads credentials from a file and performs the OAuth2 authentication flow.
This is useful for one-time authentication setup.
Example:
token, err := email.AuthenticateGmailFromFile("credentials.json")
if err != nil {
log.Fatal(err)
}
// Save the token for future use
err = os.WriteFile("token.json", token, 0600)
if err != nil {
log.Fatal(err)
}
func QuickSend ¶
QuickSend provides a simple way to send an email with minimal configuration. This is useful for simple use cases where you don't need to reuse the client.
Example:
err := email.QuickSend("gmail",
&email.GmailConfig{
CredentialsJSON: creds,
TokenJSON: token,
},
"from@example.com",
"to@example.com",
"Subject",
"Body")
Types ¶
type Attachment ¶
type Attachment struct {
// Filename is the name of the file as it will appear in the email
Filename string
// Content is the file content as bytes
Content []byte
// MimeType is the MIME type of the file (optional).
// If empty, it will be automatically detected based on the filename.
MimeType string
}
Attachment represents a file attachment for an email.
type AttachmentMeta ¶ added in v1.1.0
type AttachmentMeta struct {
// ID is the provider attachment identifier (used to fetch content).
ID string
// Filename is the attachment's file name.
Filename string
// MimeType is the attachment's content type.
MimeType string
// Size is the attachment size in bytes (0 if unknown).
Size int64
}
AttachmentMeta describes an attachment without downloading its content.
type CalendarProvider ¶ added in v1.3.0
type CalendarProvider interface {
// ListEvents returns events in the option's date range, soonest first.
ListEvents(ctx context.Context, opts EventListOptions) ([]Event, error)
// ReadEvent returns one event by id, including body and attendees.
ReadEvent(ctx context.Context, id string) (*Event, error)
// CreateEvent creates an event and returns it with its assigned id.
CreateEvent(ctx context.Context, e Event) (*Event, error)
// UpdateEvent applies the non-zero fields of e to the event with the given
// id and returns the updated event. Categories, when non-nil, REPLACE the
// event's category list wholesale (Graph PATCH semantics).
UpdateEvent(ctx context.Context, id string, e Event) (*Event, error)
// DeleteEvent removes an event by id.
DeleteEvent(ctx context.Context, id string) error
}
CalendarProvider is implemented by providers that support calendar operations (Outlook 365). All methods take a context and act on the mailbox the provider was configured for (OutlookConfig.UserID).
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is the main email client that wraps a provider implementation. It is thread-safe and can be used concurrently.
func NewClient ¶
NewClient creates a new email client with the specified configuration. It returns an error if the configuration is invalid or the provider fails to initialize.
Example:
config := &email.Config{
Provider: "gmail",
Gmail: &email.GmailConfig{
CredentialsJSON: credentialsJSON,
TokenJSON: tokenJSON,
},
}
client, err := email.NewClient(config)
func QuickClientFromEnv ¶
QuickClientFromEnv creates a client using environment variables. This combines ConfigFromEnv and NewClient for convenience.
Example:
client, err := email.QuickClientFromEnv()
if err != nil {
log.Fatal(err)
}
err = client.Send(&email.Message{
From: "sender@example.com",
To: []string{"recipient@example.com"},
Subject: "Test",
Body: "Hello!",
})
func (*Client) AddAttachment ¶ added in v1.5.0
func (c *Client) AddAttachment(id string, att Attachment) error
AddAttachment adds a single file attachment to an existing message (e.g. a draft), with a default timeout. See MailboxProvider.AddAttachment.
func (*Client) AddAttachmentWithContext ¶ added in v1.5.0
AddAttachmentWithContext is AddAttachment with a caller-supplied context.
func (*Client) CreateEvent ¶ added in v1.3.0
CreateEvent creates a calendar event (context.Background).
func (*Client) CreateEventWithContext ¶ added in v1.3.0
CreateEventWithContext creates a calendar event.
func (*Client) CreateMasterCategory ¶ added in v1.5.0
CreateMasterCategory adds a category to the mailbox's master list, with a default timeout. See MailboxProvider.CreateMasterCategory.
func (*Client) CreateMasterCategoryWithContext ¶ added in v1.5.0
CreateMasterCategoryWithContext is CreateMasterCategory with a caller-supplied context.
func (*Client) CreateReplyDraft ¶ added in v1.4.0
CreateReplyDraft creates a draft reply to a message and returns the draft's id and web link, with a default timeout. See MailboxProvider.CreateReplyDraft.
func (*Client) CreateReplyDraftWithContext ¶ added in v1.4.0
func (c *Client) CreateReplyDraftWithContext(ctx context.Context, id, htmlBody string, replyAll bool) (*DraftInfo, error)
CreateReplyDraftWithContext is CreateReplyDraft with a caller-supplied context.
func (*Client) Delete ¶ added in v1.1.0
Delete removes a message (trash if permanent is false), with a default timeout.
func (*Client) DeleteEvent ¶ added in v1.3.0
DeleteEvent deletes a calendar event (context.Background).
func (*Client) DeleteEventWithContext ¶ added in v1.3.0
DeleteEventWithContext deletes a calendar event.
func (*Client) DeleteWithContext ¶ added in v1.2.0
DeleteWithContext is Delete with a caller-supplied context.
func (*Client) List ¶ added in v1.1.0
func (c *Client) List(opts ListOptions) ([]Summary, error)
List returns message headers from a folder (default inbox), newest first, with a default timeout.
func (*Client) ListAttachments ¶ added in v1.1.0
func (c *Client) ListAttachments(id string) ([]AttachmentMeta, error)
ListAttachments returns metadata for a message's file attachments, with a default timeout.
func (*Client) ListAttachmentsWithContext ¶ added in v1.2.0
func (c *Client) ListAttachmentsWithContext(ctx context.Context, id string) ([]AttachmentMeta, error)
ListAttachmentsWithContext is ListAttachments with a caller-supplied context.
func (*Client) ListEvents ¶ added in v1.3.0
func (c *Client) ListEvents(opts EventListOptions) ([]Event, error)
ListEvents lists calendar events in the given range (context.Background).
func (*Client) ListEventsWithContext ¶ added in v1.3.0
ListEventsWithContext lists calendar events in the given range.
func (*Client) ListFolders ¶ added in v1.1.0
ListFolders returns the mailbox's folders (Outlook) or labels (Gmail), with a default timeout.
func (*Client) ListFoldersWithContext ¶ added in v1.2.0
ListFoldersWithContext is ListFolders with a caller-supplied context.
func (*Client) ListMasterCategories ¶ added in v1.5.0
func (c *Client) ListMasterCategories() ([]MasterCategory, error)
ListMasterCategories returns the mailbox's master category list, with a default timeout. See MailboxProvider.ListMasterCategories.
func (*Client) ListMasterCategoriesWithContext ¶ added in v1.5.0
func (c *Client) ListMasterCategoriesWithContext(ctx context.Context) ([]MasterCategory, error)
ListMasterCategoriesWithContext is ListMasterCategories with a caller-supplied context.
func (*Client) ListWithContext ¶ added in v1.1.0
ListWithContext is List with a caller-supplied context.
func (*Client) MarkRead ¶ added in v1.1.0
MarkRead sets a message's read state, with a default timeout.
func (*Client) MarkReadWithContext ¶ added in v1.2.0
MarkReadWithContext is MarkRead with a caller-supplied context.
func (*Client) Move ¶ added in v1.1.0
Move relocates a message to the destination folder/label, with a default timeout. See MailboxProvider.Move for Gmail's archive-style semantics.
func (*Client) MoveWithContext ¶ added in v1.1.0
MoveWithContext is Move with a caller-supplied context.
func (*Client) Read ¶ added in v1.1.0
func (c *Client) Read(id string) (*FullMessage, error)
Read returns one message including its body, with a default timeout.
func (*Client) ReadEventWithContext ¶ added in v1.3.0
ReadEventWithContext reads one event by id.
func (*Client) ReadWithContext ¶ added in v1.1.0
ReadWithContext is Read with a caller-supplied context.
func (*Client) SaveAttachments ¶ added in v1.1.0
SaveAttachments writes a message's file attachments into destDir, with a default timeout, and returns the paths written.
func (*Client) SaveAttachmentsWithContext ¶ added in v1.1.0
func (c *Client) SaveAttachmentsWithContext(ctx context.Context, id, destDir string) ([]string, error)
SaveAttachmentsWithContext is SaveAttachments with a caller-supplied context.
func (*Client) SaveMessageRaw ¶ added in v1.3.2
SaveMessageRaw writes a message's raw RFC822 MIME (.eml) into destDir under a collision-free name derived from baseName, with a default timeout, and returns the path written. See MailboxProvider.SaveMessageRaw.
func (*Client) SaveMessageRawWithContext ¶ added in v1.3.2
func (c *Client) SaveMessageRawWithContext(ctx context.Context, id, destDir, baseName string) (string, error)
SaveMessageRawWithContext is SaveMessageRaw with a caller-supplied context.
func (*Client) Search ¶ added in v1.1.0
func (c *Client) Search(query string, opts ListOptions) ([]Summary, error)
Search runs a provider-native full-text search, with a default timeout.
func (*Client) SearchWithContext ¶ added in v1.1.0
func (c *Client) SearchWithContext(ctx context.Context, query string, opts ListOptions) ([]Summary, error)
SearchWithContext is Search with a caller-supplied context.
func (*Client) Send ¶
Send sends an email message with a default timeout of 30 seconds. It validates the message before sending and returns an error if validation fails or the send operation fails.
func (*Client) SendWithContext ¶
SendWithContext sends an email message with a custom context. This allows for custom timeouts, cancellation, and passing request-scoped values. The message is validated before sending.
Example:
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() err := client.SendWithContext(ctx, msg)
func (*Client) SetLabels ¶ added in v1.1.0
SetLabels replaces a message's labels/categories, with a default timeout.
func (*Client) SetLabelsWithContext ¶ added in v1.2.0
SetLabelsWithContext is SetLabels with a caller-supplied context.
func (*Client) UpdateEvent ¶ added in v1.3.0
UpdateEvent updates a calendar event (context.Background).
type Config ¶
type Config struct {
// Provider specifies which email provider to use.
// Supported values: "outlook365", "gmail"
Provider string
// Outlook contains Outlook 365 specific configuration.
// Required when Provider is "outlook365".
Outlook *OutlookConfig
// Gmail contains Gmail specific configuration.
// Required when Provider is "gmail".
Gmail *GmailConfig
// Custom is reserved for future provider extensions
Custom map[string]interface{}
}
Config holds the configuration for creating email providers. Only one provider configuration should be set.
func ConfigFromEnv ¶
ConfigFromEnv creates an email configuration from environment variables. This is a convenient way to configure the email client without hardcoding credentials.
Environment variables:
- EMAIL_PROVIDER: The email provider to use ("outlook365" or "gmail"), defaults to "outlook365"
- For Outlook 365:
- OUTLOOK_TENANT_ID: Azure AD tenant ID (required)
- OUTLOOK_CLIENT_ID: Azure AD application client ID (required)
- OUTLOOK_CLIENT_SECRET: Azure AD application client secret (required)
- For Gmail:
- GMAIL_CREDENTIALS_FILE: Path to the OAuth2 credentials JSON file (required)
- GMAIL_TOKEN_FILE: Path to the OAuth2 token JSON file (defaults to "token.json")
Example:
os.Setenv("EMAIL_PROVIDER", "gmail")
os.Setenv("GMAIL_CREDENTIALS_FILE", "credentials.json")
os.Setenv("GMAIL_TOKEN_FILE", "token.json")
config, err := email.ConfigFromEnv()
if err != nil {
log.Fatal(err)
}
type DraftInfo ¶ added in v1.4.0
type DraftInfo struct {
// ID is the provider message id of the created draft (Graph message id for
// Outlook). Treat it as opaque; it can be passed to Read/Delete/Move.
ID string
// WebLink is the Outlook-on-the-web URL to open the draft, if the provider
// returns one (empty otherwise).
WebLink string
}
DraftInfo identifies a draft message created by CreateReplyDraft, with enough for a caller to report it or open it. The draft lives in the mailbox's Drafts folder until sent; go-email does not send it.
type Event ¶ added in v1.3.0
type Event struct {
// ID is the provider event identifier. Treat it as opaque. Empty when an
// Event is passed to Create (the provider assigns it).
ID string
// Subject is the event title.
Subject string
// Start, End are the event's wall-clock start/end in TimeZone.
Start time.Time
End time.Time
// TimeZone is the IANA zone the Start/End are expressed in (e.g.
// "Australia/Perth"). Empty on input means the provider's default zone.
TimeZone string
// AllDay reports an all-day event (Start/End are dates).
AllDay bool
// Location is the free-text venue, if any.
Location string
// BodyText is the plain-text event description.
BodyText string
// Organizer is the organiser's email address (output only).
Organizer string
// Attendees holds attendee email addresses.
Attendees []string
// Categories holds the event's Outlook category tags.
Categories []string
}
Event is a calendar event. Times are wall-clock values paired with TimeZone (an IANA name, e.g. "Australia/Perth"); for an all-day event the Start/End carry the date with a zero time-of-day and AllDay is true.
type EventListOptions ¶ added in v1.3.0
type EventListOptions struct {
// Start, End bound the query (inclusive of events overlapping the range).
// Zero Start means "now"; zero End means "no upper bound" for the
// upcoming-events list.
Start time.Time
End time.Time
// Limit caps the number of events returned (0 = provider default).
Limit int
}
EventListOptions bounds a calendar list query to a date range.
type Folder ¶ added in v1.1.0
type Folder struct {
// ID is the provider identifier, suitable for ListOptions.Folder and Move.
ID string
// Name is the human-readable display name.
Name string
// Unread is the count of unread items, where the provider reports it
// (Outlook); 0 if unknown.
Unread int
}
Folder is a mail folder (Outlook) or label (Gmail).
type FullMessage ¶ added in v1.1.0
type FullMessage struct {
Summary
// To, Cc are the recipient addresses.
To []string
Cc []string
// BodyText is the plain-text body, if available.
BodyText string
// BodyHTML is the HTML body, if the message was HTML.
BodyHTML string
}
FullMessage is a message with its body, returned by Read.
type GmailAuthHelper ¶
type GmailAuthHelper struct {
// CredentialsJSON contains the OAuth2 client credentials from Google Cloud Console
CredentialsJSON []byte
// Scopes overrides the OAuth2 scopes requested during the consent flow.
// If empty, the helper requests gmail.send + gmail.modify so the resulting
// token works for both sending and the MailboxProvider read/move/label
// operations. Set this (e.g. add gmail.MailGoogleComScope) before calling
// Authenticate to widen or narrow the grant.
Scopes []string
}
GmailAuthHelper provides utilities for Gmail OAuth2 authentication. It handles the OAuth2 flow for obtaining access tokens for Gmail API.
func NewGmailAuthHelper ¶
func NewGmailAuthHelper(credentialsJSON []byte) *GmailAuthHelper
NewGmailAuthHelper creates a new Gmail authentication helper with the provided credentials.
The credentials should be the JSON file downloaded from Google Cloud Console when creating OAuth2 credentials for a desktop application.
Example:
creds, err := os.ReadFile("credentials.json")
if err != nil {
log.Fatal(err)
}
helper := email.NewGmailAuthHelper(creds)
func (*GmailAuthHelper) Authenticate ¶
func (g *GmailAuthHelper) Authenticate() ([]byte, error)
Authenticate performs the OAuth2 authentication flow and returns the access token as JSON. This method will prompt the user to visit a URL and enter an authorization code.
The returned token can be saved and reused for future email sending without requiring re-authentication.
Example:
helper := email.NewGmailAuthHelper(credentialsJSON)
token, err := helper.Authenticate()
if err != nil {
log.Fatal(err)
}
// Save token for future use
err = os.WriteFile("token.json", token, 0600)
type GmailConfig ¶
type GmailConfig struct {
// CredentialsJSON contains the OAuth2 credentials downloaded from Google Cloud Console
CredentialsJSON []byte
// TokenJSON contains the stored OAuth2 token.
// If not provided, authentication will be required on first use.
TokenJSON []byte
// Scopes overrides the OAuth2 scopes requested. If empty, the provider
// requests gmail.send + gmail.modify, which covers sending plus the
// MailboxProvider read/move/label/trash operations. Add
// gmail.MailGoogleComScope (full access) here if you need permanent
// deletion. Widening scopes requires re-running the consent flow and
// replacing the stored token.
Scopes []string
}
GmailConfig holds Gmail specific configuration for OAuth2 authentication.
type ListOptions ¶ added in v1.1.0
type ListOptions struct {
// Folder is the folder/label to list. Empty means "inbox". For Outlook
// this is a well-known folder name or folder id (e.g. "inbox",
// "sentitems", "archive"); for Gmail it is a label name or system label
// id (e.g. "INBOX", "SENT"). Ignored by Search.
Folder string
// UnreadOnly restricts results to unread messages.
UnreadOnly bool
// Since restricts results to messages received at or after this time.
// The zero value means no lower bound.
Since time.Time
// Limit caps the number of messages returned (0 means provider default,
// no explicit cap). Acts as a ceiling across pages.
Limit int
}
ListOptions filters and bounds a List or Search call. The zero value lists the inbox with provider defaults.
type MailboxProvider ¶ added in v1.1.0
type MailboxProvider interface {
Provider
// List returns message headers from a folder per opts (default inbox),
// newest first.
List(ctx context.Context, opts ListOptions) ([]Summary, error)
// Read returns one message including its body.
Read(ctx context.Context, id string) (*FullMessage, error)
// Search runs a provider-native full-text search. The query uses the
// provider's own syntax (Graph $search KQL / Gmail search operators).
// opts bounds the results (Folder is ignored).
Search(ctx context.Context, query string, opts ListOptions) ([]Summary, error)
// Move relocates a message to the destination folder/label. For Outlook
// the message is moved; for Gmail the destination label is added and
// INBOX removed (archive-style move). dest is a folder/label name or id.
Move(ctx context.Context, id, dest string) error
// ListAttachments returns metadata for a message's file attachments.
ListAttachments(ctx context.Context, id string) ([]AttachmentMeta, error)
// SaveAttachments writes a message's file attachments into destDir and
// returns the paths written. destDir is created if it does not exist.
SaveAttachments(ctx context.Context, id, destDir string) ([]string, error)
// SaveMessageRaw writes the message's raw RFC822 MIME (.eml) into destDir
// under a collision-free name derived from baseName (".eml" is appended if
// absent; reserved/control chars in baseName are sanitized), and returns the
// path written. destDir is created if it does not exist. The raw MIME is the
// provider's verbatim wire form (Graph $value / Gmail raw), suitable for an
// .eml->PDF converter. Providers that cannot export raw MIME return
// ErrUnsupported.
SaveMessageRaw(ctx context.Context, id, destDir, baseName string) (string, error)
// CreateReplyDraft creates a DRAFT reply to the message identified by id and
// returns the new draft's id and web link. It never sends. The server
// (Graph createReply / createReplyAll) seeds the draft with the correct
// recipients and the quoted original message, preserving thread context;
// htmlBody is then PREPENDED to that quoted content as the reply text, so the
// caller's content appears above the quoted original. htmlBody is always
// treated as HTML (so formatted tables render); pass "" to create a draft
// containing only the quoted original. If replyAll is true the draft replies
// to all recipients (To + Cc of the original), preserving the CCs; otherwise
// it replies to the sender only. Providers that cannot create draft replies
// return ErrUnsupported.
CreateReplyDraft(ctx context.Context, id, htmlBody string, replyAll bool) (*DraftInfo, error)
// AddAttachment adds a single file attachment to an EXISTING message (typically
// a draft, e.g. one from CreateReplyDraft) identified by id. att.Filename names
// the file and att.Content carries its raw bytes; att.MimeType is used verbatim
// when set, otherwise it is detected from the filename extension. This is a
// DIRECT attachment upload (a single POST of the whole content), so it is
// bounded to small files: content larger than MaxDirectAttachmentBytes is
// rejected with an error rather than attempted, because Graph requires an upload
// session for larger attachments (which is out of scope). It never sends the
// message. Providers that cannot add attachments to a stored message return
// ErrUnsupported.
AddAttachment(ctx context.Context, id string, att Attachment) error
// ListMasterCategories returns the mailbox's master category list — the named,
// colored categories Outlook renders as chips (outlook/masterCategories). A
// caller ensuring a category exists should fetch this once and match
// case-insensitively (master categories are unique case-insensitively).
// Providers without a master-category concept (Gmail) return ErrUnsupported.
ListMasterCategories(ctx context.Context) ([]MasterCategory, error)
// CreateMasterCategory adds a category to the mailbox's master list so Outlook
// renders it in color. name is the display name; color is an Outlook preset
// token ("preset0".."preset24"); an empty/invalid color falls back to a default
// preset. The caller is responsible for the case-insensitive existence check —
// creating a name that already exists (in any case) fails with a Graph 409.
// Providers without a master-category concept (Gmail) return ErrUnsupported.
CreateMasterCategory(ctx context.Context, name, color string) error
// MarkRead sets a message's read state.
MarkRead(ctx context.Context, id string, read bool) error
// SetLabels replaces a message's labels (Gmail) or categories (Outlook)
// with the given set. Names are used; for Gmail, missing user labels are
// created on demand.
SetLabels(ctx context.Context, id string, labels []string) error
// Delete removes a message. If permanent is false the message is moved to
// the trash/deleted-items and is recoverable; if true it is permanently
// deleted where the provider supports it (Gmail requires the full-access
// scope for permanent deletion).
Delete(ctx context.Context, id string, permanent bool) error
// ListFolders returns the mailbox's folders (Outlook) or labels (Gmail).
ListFolders(ctx context.Context) ([]Folder, error)
}
MailboxProvider extends Provider with read and management operations. Both built-in providers (Outlook 365, Gmail) implement it. Code that only sends can continue to use Provider; code that needs the wider surface can type-assert a provider to MailboxProvider or use the Client methods below, which require it.
All methods take a context for timeout/cancellation. The mailbox operated on is the one the provider was configured for (Outlook: the address passed as the message From / the configured user; Gmail: the authenticated "me").
type MasterCategory ¶ added in v1.5.0
type MasterCategory struct {
// DisplayName is the category name (e.g. "e-Filed"). Master categories are
// unique case-insensitively within a mailbox.
DisplayName string
// Color is the Outlook preset color token ("preset0".."preset24", or "none").
// preset0 is red and preset4 is green in the standard Outlook palette.
Color string
}
MasterCategory is one entry in a mailbox's master category list (Outlook's named, colored categories under outlook/masterCategories). Only master-list entries render as colored chips in Outlook; a category applied to a message that is NOT in this list shows without a color. Gmail has no equivalent.
type Message ¶
type Message struct {
// From is the sender's email address (required)
From string
// To contains the primary recipient email addresses (at least one required)
To []string
// Cc contains carbon copy recipient email addresses (optional)
Cc []string
// Bcc contains blind carbon copy recipient email addresses (optional)
Bcc []string
// Subject is the email subject line (required)
Subject string
// Body is the email content (required)
Body string
// HTML indicates whether the body should be treated as HTML.
// If false, the body is treated as plain text.
HTML bool
// Attachments contains file attachments (optional)
Attachments []Attachment
}
Message represents an email message with all necessary fields for sending.
type OutlookConfig ¶
type OutlookConfig struct {
// TenantID is the Azure AD tenant ID
TenantID string
// ClientID is the Azure AD application client ID
ClientID string
// ClientSecret is the Azure AD application client secret
ClientSecret string
// UserID is the mailbox (user principal name or object id) that the read
// and management operations of MailboxProvider act on, e.g.
// "info@deltalegal.com.au". It is not required for sending — Send keys off
// the message's From address — but the mailbox operations (List, Read,
// Move, ...) need a concrete target and return an error if it is empty.
UserID string
}
OutlookConfig holds Outlook 365 specific configuration for OAuth2 authentication.
type Provider ¶
type Provider interface {
// Send sends an email message using the provider's implementation.
// The context can be used for timeout and cancellation.
Send(ctx context.Context, msg *Message) error
}
Provider is the interface that all email providers must implement. This allows for easy addition of new email providers.
type Summary ¶ added in v1.1.0
type Summary struct {
// ID is the provider-specific message identifier. For Outlook it is the
// Graph message id; for Gmail the Gmail message id. Treat it as opaque.
ID string
// ThreadID groups a conversation. Populated for Gmail; empty for Outlook
// (Outlook exposes conversationId, which is not currently surfaced).
ThreadID string
// From is the sender's email address.
From string
// Subject is the message subject (UTF-8, may be non-ASCII).
Subject string
// Received is the time the message was received.
Received time.Time
// HasAttachments reports whether the message carries file attachments.
HasAttachments bool
// Unread reports whether the message is unread.
Unread bool
// Labels holds the message's labels (Gmail) or categories (Outlook).
Labels []string
}
Summary is a lightweight message header returned by List and Search. It deliberately omits the body so listing a folder is cheap.
type VersionInfo ¶
type VersionInfo struct {
Version string `json:"version"`
Major int `json:"major"`
Minor int `json:"minor"`
Patch int `json:"patch"`
PreRelease string `json:"preRelease,omitempty"`
BuildDate string `json:"buildDate,omitempty"`
GitCommit string `json:"gitCommit,omitempty"`
}
VersionInfo contains detailed version information
func GetVersionInfo ¶
func GetVersionInfo() VersionInfo
GetVersionInfo returns detailed version information