Documentation
¶
Overview ¶
Package gmail is a thin wrapper around the official Gmail API client (google.golang.org/api/gmail/v1) - unlike core/msgraph, there's a real SDK to build on here, so this package's job is the higher-level orchestration around it (history-based sync with bootstrap fallback, MIME parsing/translation, reply threading), not raw HTTP.
Shared, tenant-agnostic logic used two ways: seshat-backend wraps it in a real inbox.Connector (internal/inbox/gmail) for the local Inbox workflow, and seshat-server wraps it in a small self-hosted MCP server (internal/server/gmailmcp) so the workspace chat agent can call it as a tool, bridged through a per-employee OAuth-connected account the same way every other agent_action connector kind works. Neither side stores or manages OAuth tokens here - callers pass an already-token-bound oauth2.TokenSource, mirroring core/msgraph's Client.
Index ¶
- Variables
- type Attachment
- type Client
- func (c *Client) Compose(ctx context.Context, opts ComposeOptions) (string, error)
- func (c *Client) CreateDraft(ctx context.Context, opts ComposeOptions) (Draft, error)
- func (c *Client) CreateLabel(ctx context.Context, name string) (Label, error)
- func (c *Client) DeleteDraft(ctx context.Context, draftID string) error
- func (c *Client) DeleteLabel(ctx context.Context, id string) error
- func (c *Client) FetchAttachment(ctx context.Context, messageID, attachmentID string) ([]byte, error)
- func (c *Client) GetMessage(ctx context.Context, id string) (Message, error)
- func (c *Client) GetProfile(ctx context.Context) (Profile, error)
- func (c *Client) GetThread(ctx context.Context, threadID string) ([]Message, error)
- func (c *Client) ListDrafts(ctx context.Context, maxResults int64) ([]Draft, error)
- func (c *Client) ListLabels(ctx context.Context) ([]Label, error)
- func (c *Client) MarkRead(ctx context.Context, id string) error
- func (c *Client) MarkThreadRead(ctx context.Context, threadID string) error
- func (c *Client) MarkThreadUnread(ctx context.Context, threadID string) error
- func (c *Client) MarkUnread(ctx context.Context, id string) error
- func (c *Client) ModifyLabels(ctx context.Context, id string, addLabelIds, removeLabelIds []string) error
- func (c *Client) ModifyThreadLabels(ctx context.Context, threadID string, addLabelIds, removeLabelIds []string) error
- func (c *Client) Reply(ctx context.Context, threadID, to, body string) (string, error)
- func (c *Client) Search(ctx context.Context, query string, maxResults int64) ([]Message, error)
- func (c *Client) Send(ctx context.Context, to []string, subject, body string) (string, error)
- func (c *Client) SendDraft(ctx context.Context, draftID string) (string, error)
- func (c *Client) Sync(ctx context.Context, cursor string) ([]Message, string, error)
- func (c *Client) TrashMessage(ctx context.Context, id string) error
- func (c *Client) TrashThread(ctx context.Context, threadID string) error
- func (c *Client) UntrashMessage(ctx context.Context, id string) error
- func (c *Client) UntrashThread(ctx context.Context, threadID string) error
- type ComposeOptions
- type Draft
- type EmailAddress
- type Label
- type Message
- type Profile
Constants ¶
This section is empty.
Variables ¶
var Scopes = []string{
"https://www.googleapis.com/auth/gmail.modify",
"https://www.googleapis.com/auth/gmail.send",
"https://www.googleapis.com/auth/userinfo.email",
}
Scopes requested from Google, matching what this client actually does. gmail.modify is a documented superset of gmail.readonly (all read/write operations except immutable delete and settings) - needed once this client started doing more than read/send: trash/untrash, mark read/ unread, label CRUD and drafts CRUD all mutate existing mail. gmail.send stays alongside it since it's the scope Send/Reply/Compose actually rely on. These are all "restricted" scopes (Google's sensitive-scope review applies), which is also why each deployment registers its own Google Cloud OAuth client and consent screen rather than sharing a public one.
Functions ¶
This section is empty.
Types ¶
type Attachment ¶
Attachment is one attachment's metadata, not its bytes - see Client.FetchAttachment for the lazy download step.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client wraps the official Gmail API client.
func New ¶
func New(ctx context.Context, tokenSource oauth2.TokenSource, opts ...option.ClientOption) (*Client, error)
New builds a Client from an oauth2.TokenSource - this package never sees a raw OAuth config or does its own token exchange. Extra opts are passed through to gmailapi.NewService, letting tests point at a fake server via option.WithHTTPClient/option.WithEndpoint.
func (*Client) Compose ¶
Compose sends a message with the full CC/BCC/threading header set ComposeOptions offers - the primitive both the richer "send" MCP tool and CreateDraft build on.
func (*Client) CreateDraft ¶
CreateDraft saves a message as a draft instead of sending it.
func (*Client) CreateLabel ¶
CreateLabel creates a new user label.
func (*Client) DeleteDraft ¶
DeleteDraft discards a draft without sending it.
func (*Client) DeleteLabel ¶
DeleteLabel deletes a user label (Gmail rejects deleting a system label with an API error, which this surfaces unchanged rather than special- casing).
func (*Client) FetchAttachment ¶
func (c *Client) FetchAttachment(ctx context.Context, messageID, attachmentID string) ([]byte, error)
FetchAttachment downloads one attachment's bytes by the GmailAttachmentID captured on its Attachment metadata at sync time.
func (*Client) GetMessage ¶
GetMessage fetches one message by ID, fully MIME-parsed - unlike Sync, this targets a specific message the caller already knows about (e.g. from a prior Search/GetThread call), not "what's new".
func (*Client) GetProfile ¶
GetProfile returns the connected account's own mailbox summary.
func (*Client) GetThread ¶
GetThread returns every message in a thread, in Gmail's own order (oldest first).
func (*Client) ListDrafts ¶
ListDrafts returns saved drafts. Gmail's Drafts.List response carries only each draft's ID/ThreadID/MessageID, not its subject or snippet - a per-draft Get call would be needed for that, which this deliberately doesn't do (N+1 for a list operation). maxResults <= 0 uses listDraftsLimit.
func (*Client) ListLabels ¶
ListLabels returns every label in the mailbox, system and user-created.
func (*Client) MarkThreadRead ¶
MarkThreadRead removes the UNREAD label from every message in a thread.
func (*Client) MarkThreadUnread ¶
MarkThreadUnread adds the UNREAD label to every message in a thread.
func (*Client) MarkUnread ¶
MarkUnread adds the UNREAD label to a message.
func (*Client) ModifyLabels ¶
func (c *Client) ModifyLabels(ctx context.Context, id string, addLabelIds, removeLabelIds []string) error
ModifyLabels adds and/or removes label IDs on a message in one call - the general primitive MarkRead/MarkUnread are built on, also usable directly for user-created labels (see ListLabels/CreateLabel).
func (*Client) ModifyThreadLabels ¶
func (c *Client) ModifyThreadLabels(ctx context.Context, threadID string, addLabelIds, removeLabelIds []string) error
ModifyThreadLabels adds and/or removes label IDs on every message in a thread in one call - the thread-level counterpart to ModifyLabels.
func (*Client) Reply ¶
Reply delivers a reply in the given Gmail thread (empty threadID sends an orphaned message instead). It first fetches the thread's most recent message (headers only) to build a properly threaded reply - matching Subject plus In-Reply-To/References - rather than sending a message that merely shares a ThreadId, which Gmail's own clients don't reliably group without those headers. Unlike Microsoft Graph's fire-and-forget reply action, Gmail's Send returns a real message ID synchronously.
func (*Client) Search ¶
Search runs a Gmail search query (the same syntax as the Gmail web UI's search box, e.g. "from:boss@company.com is:unread") and returns the matching messages, most recent first. maxResults <= 0 uses searchMessageLimit.
func (*Client) SendDraft ¶
SendDraft sends a previously saved draft and returns the sent message ID.
func (*Client) Sync ¶
Sync pulls everything new since cursor and returns it plus the cursor value to persist for next time. An empty cursor bootstraps (a snapshot of recent messages); a history-expired cursor (Gmail only retains history for ~7 days) transparently falls back to a fresh bootstrap instead of surfacing an error the caller can't act on.
func (*Client) TrashMessage ¶
TrashMessage moves a message to Trash (Gmail permanently deletes trashed mail after ~30 days) - reversible via UntrashMessage until then.
func (*Client) TrashThread ¶
TrashThread moves every message in a thread to Trash in one call - Gmail's own thread-level primitive (Outlook has no equivalent; a caller there must enumerate the conversation's messages and move each individually, see coremsgraph's ListMessagesInConversation).
func (*Client) UntrashMessage ¶
UntrashMessage removes a message from Trash.
type ComposeOptions ¶
type ComposeOptions struct {
To, Cc, Bcc []string
Subject string
Body string
// ThreadID, when set, sends within an existing thread - Gmail groups by
// ThreadId server-side, unlike Reply which also rewrites Subject/
// In-Reply-To/References from the thread's last message.
ThreadID string
}
ComposeOptions is the full header set a caller-composed (non-reply) message can set - CC/BCC/threading, unlike Send's minimal to/subject/body (kept as-is since inbox/gmail's connector and Send's own existing callers only ever need that minimal shape).
type Draft ¶
Draft is a saved-but-unsent message. Gmail's own Drafts.List only ever returns the ID/ThreadID/MessageID triple (no subject/snippet) without a separate per-draft Get call, so that's all this carries - see Client.ListDrafts's doc comment.
type EmailAddress ¶
EmailAddress is a parsed "Name <address>" (or bare address) header value.
type Label ¶
Label is a Gmail label (system or user-created) - see Client.ListLabels/CreateLabel/DeleteLabel.
type Message ¶
type Message struct {
ID string
ThreadID string
Subject string
From EmailAddress
To EmailAddress
BodyText string
Attachments []Attachment
SentAt time.Time
// LabelIds are Gmail's own label IDs (both system, e.g. "UNREAD"/"INBOX"/
// "TRASH", and user-created ones) - the raw source Unread and label-
// management operations (Client.ModifyLabels et al.) read/write.
LabelIds []string
// Unread mirrors whether LabelIds contains "UNREAD", computed once at
// translation time so callers don't need to know Gmail's label-based
// read/unread convention themselves.
Unread bool
}
Message is one email, already MIME-parsed. Deliberately has no "direction" field - inbound vs. outbound depends on which address is the connected account's own, an inbox-domain concept the caller (e.g. inbox/gmail.translateMessage) computes itself from From/To.