store

package
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2026 License: AGPL-3.0 Imports: 19 Imported by: 0

Documentation

Overview

Package store owns the SQLite database: opening it, migrating it and, later, the repositories.

Index

Constants

View Source
const (
	ActionDone   = "done"
	ActionDryRun = "dry_run"
	ActionFailed = "failed"
	ActionUndone = "undone"
)

Action statuses, as stored in actions.status.

View Source
const (
	BatchLive       = "live"
	BatchCleanup    = "cleanup"
	BatchCorrection = "correction"
	BatchUndo       = "undo"

	BatchRunning = "running"
	BatchDone    = "done"
	BatchFailed  = "failed"
	BatchUndone  = "undone"
)

Batch kinds and statuses, as stored in batches.

View Source
const (
	SettingTrashToFolder = "trash_to_folder"
	DefaultTrashToFolder = true
)

SettingTrashToFolder is the settings key of the switch that sends trashed mail to an ordinary folder of MailRules' own instead of the server's Trash, which providers empty on their own. DefaultTrashToFolder is in force until it is set, for a new install and an existing one alike.

View Source
const (
	SettingLeaveOwnMail = "leave_own_mail"
	DefaultLeaveOwnMail = true
)

SettingLeaveOwnMail is the settings key of the switch that leaves mail sent from the mailbox's own address alone (Account.OwnAddresses): no rule, model or action touches it. DefaultLeaveOwnMail is in force until it is set.

View Source
const (
	StateNew     = "new"     // ingested, not yet decided
	StateDecided = "decided" // a decision is recorded, its actions are not finished
	StateActed   = "acted"   // the decision's actions ran (or were recorded as dry-run)
	StateReview  = "review"  // waiting in Needs review
	StateSkipped = "skipped" // nothing to do: no rule matched, or the message was gone
	StateError   = "error"   // failed; the retry job runs it again at NextAttemptAt
)

Message states, as stored in messages.state.

View Source
const (
	CorrectionFix    = "correction"
	CorrectionReview = "review"
)

What a correction was: a fix made from the feed, or an answer given in Needs review.

View Source
const (
	SummarySending = "sending"
	SummarySent    = "sent"
	SummaryFailed  = "failed"
)

Summary statuses, as stored in summaries.status.

View Source
const KeepMessagesDays = 180

KeepMessagesDays is how long a message's row is kept. After that it is deleted with its decisions and actions, unless one of its actions can still be undone: a move or a flag change that is in effect and was made in the last UndoDays. A recorded "keep" changed nothing worth putting back, so it does not hold a row.

View Source
const SettingSummary = "summary"

SettingSummary holds the summary email's settings, one JSON object (internal/summary).

View Source
const UndoDays = 30

UndoDays is how long an action stays undoable. Older ones are history: undo refuses them (actions.ErrTooOld) and they no longer hold their message's row.

Variables

View Source
var ErrAlreadySetUp = errors.New("already set up")

ErrAlreadySetUp means the first-run admin account already exists.

View Source
var ErrNotFound = errors.New("not found")

ErrNotFound means the row does not exist (or, for a session, has expired).

View Source
var ErrRuleSet = errors.New("the list must name every rule exactly once")

ErrRuleSet means a reorder did not name each of the user's rules exactly once.

View Source
var Outcomes = []string{"sorted", "trashed", "inbox", "review"}

Outcomes are the four places a processed message ends up: the groups of the Overview bar (Totals.Went*) and the values of ActivityFilter.Outcome.

Functions

func Migrate

func Migrate(ctx context.Context, db *sql.DB) error

Migrate applies every embedded migration that has not run yet.

func Open

func Open(ctx context.Context, dataDir string) (*sql.DB, error)

Open opens (creating if needed) the database in dataDir, in WAL mode.

Types

type Account

type Account struct {
	ID           int64
	UserID       int64
	Label        string
	Preset       string
	Host         string
	Port         int
	TLSMode      string
	Username     string
	WatchFolder  string
	Status       string // new | live | reconnecting | auth_failed | error | paused
	LastError    string
	LastEventAt  int64
	Capabilities []string
	CreatedAt    int64
}

Account is one mailbox the daemon watches. Its password is never part of this struct: read it with AccountSecret at the moment a connection is made.

func (Account) OwnAddresses

func (a Account) OwnAddresses() []string

OwnAddresses returns the account's own email addresses, lowercase: the username when it is a full address and, for a provider whose domains are known (presets.Preset.Domains), the name before "@" at each of them, so an iCloud username of only "jane" is jane@icloud.com, jane@me.com and jane@mac.com. Aliases are not known. It is nil when no address can be told: a username without "@" on a provider whose domains are not known.

type Action

type Action struct {
	ID         int64
	DecisionID int64 // 0 = none
	BatchID    int64 // 0 = none
	MessageID  int64
	AccountID  int64
	Kind       string // move | trash | archive | junk | flag | unflag | read | unread | keep | review
	Folder     string // params.folder: the destination of a move, or of a trash sent to MailRules' own folder (trash_to_folder)
	Before     Snapshot
	After      *Snapshot // nil unless the action is done (or was, before an undo)
	Status     string
	Error      string
	CreatedAt  int64
	UndoneAt   int64
}

Action is one row of actions: one step taken (or, in dry-run, not taken) on a message.

type ActivityFilter

type ActivityFilter struct {
	ID        int64 // only this message
	AccountID int64
	RuleID    int64
	Stage     string
	State     string // a message state; StateReview lists Needs review
	Action    string // an action kind: only messages with such an action
	Outcome   string // one of Outcomes: where the message ended up, as the stats count it
	After     FeedCursor
	Limit     int // 0 = 50
}

ActivityFilter narrows Activity. Zero fields do not filter.

type ActivityRow

type ActivityRow struct {
	Message  Message
	Decision *Decision
	Actions  []Action
}

ActivityRow is a message with its latest decision and every action taken on it, oldest first; Decision is nil until one is made.

func (ActivityRow) Cursor

func (r ActivityRow) Cursor() FeedCursor

Cursor is the position of this row in the feed, for the page after it.

type Batch

type Batch struct {
	ID        int64
	Kind      string // live | cleanup | review | correction | undo
	Status    string // running | done | failed | undone
	Total     int    // how many items the batch set out to handle; 0 = not counted (live)
	Done      int    // how many it has handled
	CreatedAt int64

	// What a cleanup batch sorted, and what its model calls have cost so far. Zero for
	// the other kinds.
	AccountID int64
	Folder    string
	Since     int64 // only mail received from this time on; 0 = all of it
	Tokens    int   // model tokens, in and out
	CostUSD   float64
	// Skipped is how many selected emails a cleanup Sort passed over because they were no
	// longer where its check found them (MAI-44). 0 for the other kinds.
	Skipped int
	// ScanLimit is the most emails the cleanup's check was allowed to cover, and ScanMatched
	// how many the folder held in the range before that cut (MAI-48). ScanLimit 0 = not
	// recorded (a batch made before this), and then ScanMatched means nothing.
	ScanLimit   int
	ScanMatched int
	// RunID joins the batches of one manual run over several mailboxes, one per mailbox
	// (MAI-43): it is the id of the run's first batch, the same on every one. 0 = not part of
	// such a run, which is every other batch and a cleanup of one mailbox.
	RunID int64
}

Batch is one row of batches: a group of actions that can be undone together.

type CleanupBatch added in v0.1.2

type CleanupBatch struct {
	AccountID int64
	Folder    string
	Since     int64 // only mail received from this time on; 0 = all of it
	// ScanLimit and ScanMatched say how much mail the check behind the batch covered: the
	// most emails it was allowed and how many the range held before that cut. ScanLimit 0
	// records nothing.
	ScanLimit, ScanMatched int
	Total                  int // emails the batch sorts
}

CleanupBatch is what a cleanup batch is opened with.

type Contact

type Contact struct {
	Address    string // lower-case
	LastSentAt int64
}

Contact is an address the account's owner has sent mail to.

type Correction

type Correction struct {
	ID          int64
	MessageID   int64
	WrongRuleID int64  // 0 = no rule had been applied
	RightRuleID int64  // 0 = keep in the inbox
	RightRule   string // that rule's name, when read back; empty for keep, or once the rule is deleted
	Kind        string // CorrectionFix | CorrectionReview; empty is stored as CorrectionFix
	Example     string // JSON message.Summary, the few-shot example
	CreatedAt   int64
}

Correction is one row of corrections: the user said a message belongs to another rule.

type Decision

type Decision struct {
	ID          int64
	MessageID   int64
	Stage       string // sender | condition | decider | fallback | none
	RuleID      int64  // 0 = no rule; in Needs review, the rule the model suggested
	RuleName    string // the rule's name: as it is now, or as it was when decided if the rule is gone
	RuleVersion int
	Confidence  float64
	Reason      string
	Model       string
	TokensIn    int
	TokensOut   int
	CostUSD     float64
	LatencyMS   int64
	CreatedAt   int64
	// Probabilities is what the decision model gave each candidate rule, by rule id, with
	// 0 for "none of these". nil when the model gives no such spread, or none was asked.
	Probabilities map[int64]float64
}

Decision is one row of decisions: what was decided for a message, and what it cost.

type FeedCursor

type FeedCursor struct {
	ActedAt int64
	ID      int64
}

FeedCursor is where a page of Activity ends: the ActedAt and ID of its last row. The next page starts after it in the feed's order. The zero value is the first page.

type Folder

type Folder struct {
	AccountID   int64
	Name        string
	Delimiter   string
	SpecialUse  string // \Junk \Trash \Archive \Sent \Drafts or empty
	UIDValidity uint32
	LastUID     uint32 // highest UID processed
}

Folder is one folder of an account, with the watch position when it is watched.

type Message

type Message struct {
	ID            int64
	AccountID     int64
	Folder        string // where it arrived; with UIDValidity and UID, the dedupe key
	UIDValidity   uint32
	UID           uint32
	MessageID     string // RFC 5322 Message-ID, without angle brackets
	FromAddr      string
	FromName      string // the From display name; may be empty
	FromDomain    string
	ToAddrs       []string
	Subject       string
	Snippet       string
	ReceivedAt    int64
	ListID        string
	HasAttachment bool
	Size          int64
	Signals       Signals
	State         string
	Attempts      int   // retries made after a failure
	NextAttemptAt int64 // 0 = not waiting for the retry job
	CreatedAt     int64
	// ActedAt is when MailRules last decided the message, or CreatedAt until a decision is
	// made. The feed is ordered by it.
	ActedAt int64

	// Where the executor last left the message; zero while it is still where it arrived.
	CurFolder      string
	CurUIDValidity uint32
	CurUID         uint32
}

Message is one row of messages: what is kept of an email. The body never is.

func (Message) Location

func (m Message) Location() mail.MsgRef

Location is where the message is on the server, as far as MailRules knows: where the executor last left it, or else where it arrived.

type NewRule

type NewRule struct {
	Rule     rules.Rule
	Position *int // 0 = first; nil, or past the end = last
}

NewRule is a rule to create and where in the priority order to put it.

type RuleStat

type RuleStat struct {
	Hits        int   // messages it was applied to since the time asked for
	LastMatchAt int64 // 0 = never
}

RuleStat is how often a rule has been applied.

type RuleUse

type RuleUse struct {
	RuleID   int64
	RuleName string
	Emails   int
	Calls    int // decisions a model was asked for
	CostUSD  float64
}

RuleUse is what one rule did, or cost. RuleID is 0 with a name for a rule that has been deleted since, and 0 without one for "no rule".

type SenderDecision

type SenderDecision struct {
	Stage      string
	RuleID     int64
	Confidence float64
	Corrected  bool // the user corrected that message afterwards
}

SenderDecision is one past decision for a sender, as the learner reads it.

type SenderSeen

type SenderSeen struct {
	Address         string
	Name            string // the From display name of the newest email; may be empty
	Messages        int    // emails seen since the time asked for
	LastSeenAt      int64
	ListUnsubscribe bool // at least one of them carried a List-Unsubscribe header
}

SenderSeen is what the messages table knows about one sender address.

type Signals

type Signals struct {
	Bulk          bool   `json:"bulk"`
	Noreply       bool   `json:"noreply"`
	Contact       bool   `json:"is_contact"`
	RepliedBefore bool   `json:"replied_before"`
	DMARC         string `json:"dmarc"`
	// ListUnsubscribe is set when the email carries a List-Unsubscribe header.
	ListUnsubscribe bool `json:"list_unsubscribe"`
}

Signals is the JSON in messages.signals.

type Snapshot

type Snapshot struct {
	Folder      string   `json:"folder"`
	UIDValidity uint32   `json:"uidvalidity"`
	UID         uint32   `json:"uid"`
	Flags       []string `json:"flags"`
}

Snapshot is the JSON in actions.before and actions.after: where a message was and which flags it had.

func (Snapshot) Ref

func (s Snapshot) Ref(accountID int64) mail.MsgRef

Ref is the snapshot's location in an account.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is the repository over the database. All times are unix seconds.

func New

func New(db *sql.DB) *Store

New wraps an open, migrated database.

func (*Store) Account

func (s *Store) Account(ctx context.Context, id int64) (Account, error)

Account returns one account, or ErrNotFound.

func (*Store) AccountSecret

func (s *Store) AccountSecret(ctx context.Context, master []byte, id int64) (string, error)

AccountSecret decrypts an account's app password or token. It returns crypto.ErrDecrypt when the master key is not the one the secret was sealed under.

func (*Store) Accounts

func (s *Store) Accounts(ctx context.Context) ([]Account, error)

Accounts lists every account, oldest first.

func (*Store) Action

func (s *Store) Action(ctx context.Context, id int64) (Action, error)

Action returns one action, or ErrNotFound.

func (*Store) Activity

func (s *Store) Activity(ctx context.Context, f ActivityFilter) ([]ActivityRow, error)

Activity lists messages, each with its latest decision, by when MailRules acted on them (ActedAt), newest first; messages acted on in the same second come newest row first.

func (*Store) ActivityFor

func (s *Store) ActivityFor(ctx context.Context, messageID int64) (ActivityRow, error)

ActivityFor returns one message's activity row, or ErrNotFound.

func (*Store) AddBatchProgress

func (s *Store) AddBatchProgress(ctx context.Context, id int64, done, skipped, tokens int, costUSD float64) error

AddBatchProgress records that a cleanup batch handled more emails, passed over some that had moved, and what their model calls cost.

func (*Store) AddCorrection

func (s *Store) AddCorrection(ctx context.Context, userID int64, c Correction, always string) (int64, error)

AddCorrection records a correction in one transaction: it inserts the row, forgets the learned sender rule for that message's sender and marks the message acted. With always (rules.MatchAddress or rules.MatchDomain; "" = neither) it also stores a user sender rule that sends the address, or its whole domain, to the right rule (or keeps it in the inbox), replacing whatever that address or domain had.

func (*Store) AddDecision

func (s *Store) AddDecision(ctx context.Context, d Decision, state string) (int64, error)

AddDecision records a decision and moves its message to state in one transaction, and returns the decision's id. The message's ActedAt becomes the decision's time.

func (*Store) AddFolder

func (s *Store) AddFolder(ctx context.Context, accountID int64, name string) error

AddFolder records a folder MailRules itself just created on the server, so it shows up before the next folder discovery. A folder already on record is left as it is.

func (*Store) AddLearnedSenderRule

func (s *Store) AddLearnedSenderRule(ctx context.Context, userID int64, address string, ruleID, now int64) (bool, error)

AddLearnedSenderRule routes an address to a rule unless the address already has a sender rule, which is never overwritten. It reports whether a rule was created.

func (*Store) AddUsage

func (s *Store) AddUsage(ctx context.Context, day, provider, model, purpose string, calls, tokensIn, tokensOut int, costUSD float64) error

AddUsage adds model calls to the day's cost-ledger row for that provider, model and purpose (decide | escalate | compose | test | cleanup), creating it if needed.

func (*Store) AdvanceFolder

func (s *Store) AdvanceFolder(ctx context.Context, ref mail.MsgRef) error

AdvanceFolder raises a watched folder's last processed UID to ref's. It never lowers it, except when the folder's UIDVALIDITY changed, where the old position means nothing.

func (*Store) Batch

func (s *Store) Batch(ctx context.Context, id int64) (Batch, error)

Batch returns one batch, or ErrNotFound.

func (*Store) BatchActionCounts

func (s *Store) BatchActionCounts(ctx context.Context, batchID int64) (map[string]int, error)

BatchActionCounts counts a batch's own actions by status.

func (*Store) BatchActions

func (s *Store) BatchActions(ctx context.Context, batchID int64) ([]Action, error)

BatchActions lists a batch's actions, oldest first.

func (*Store) Batches

func (s *Store) Batches(ctx context.Context, kind string, before int64, limit int) ([]Batch, error)

Batches lists batches, newest first: of one kind, or of every kind when kind is empty. before is the cursor (only batches with a smaller id; 0 = from the newest).

func (*Store) ClaimSummary

func (s *Store) ClaimSummary(ctx context.Context, userID, dueAt, now int64, maxAttempts int) (id int64, attempt int, ok bool, err error)

ClaimSummary records an attempt at the summary due at dueAt and reports whether this caller is to make it, and which attempt it is. It is one statement, so of two callers at once only one gets it. It is not to be made when it was sent, when an attempt is running or was cut off (status sending), after maxAttempts, or while the last failed attempt is younger than its back-off: a minute after the first, doubling each time.

func (*Store) Corrections

func (s *Store) Corrections(ctx context.Context, userID int64, limit int) ([]Correction, error)

Corrections lists the user's newest corrections, newest first, with the example each stored: the pool few-shot retrieval ranks.

func (*Store) CountMessages

func (s *Store) CountMessages(ctx context.Context, state string) (int, error)

CountMessages counts the messages in a state, e.g. StateReview for the Needs review badge.

func (*Store) CreateAccount

func (s *Store) CreateAccount(ctx context.Context, master []byte, a Account, secret string) (Account, error)

CreateAccount stores an account with its secret encrypted under the master key. The ciphertext is bound to the row id, which only exists after the insert, so the row is inserted, sealed and updated in one transaction: no reader ever sees it without a secret.

func (*Store) CreateBatch

func (s *Store) CreateBatch(ctx context.Context, kind, status string, now int64) (int64, error)

CreateBatch opens a batch and returns its id.

func (*Store) CreateCleanupBatch

func (s *Store) CreateCleanupBatch(ctx context.Context, accountID int64, folder string, since int64, scanLimit, scanMatched, total int, now int64) (Batch, error)

CreateCleanupBatch opens the batch of one cleanup run over total emails, running.

scanLimit and scanMatched say how much mail the check behind it covered: the most emails it was allowed and how many the range held before that cut. scanLimit 0 records nothing.

func (*Store) CreateCleanupRun added in v0.1.2

func (s *Store) CreateCleanupRun(ctx context.Context, cs []CleanupBatch, now int64) ([]Batch, error)

CreateCleanupRun opens one running cleanup batch per mailbox for a manual run over several mailboxes, all or none: they share a RunID, the id of the first, so they read back as one run. A run of one mailbox is a plain cleanup batch with no RunID.

func (*Store) CreateFirstUser

func (s *Store) CreateFirstUser(ctx context.Context, email, passwordHash string, now int64) (User, error)

CreateFirstUser inserts the admin account only if no user exists yet.

func (*Store) CreateRule

func (s *Store) CreateRule(ctx context.Context, r rules.Rule, now int64) (rules.Rule, error)

CreateRule inserts a rule at version 1 and returns it with its id. The caller validates the rule first.

func (*Store) CreateRules

func (s *Store) CreateRules(ctx context.Context, userID int64, added []NewRule, now int64) ([]rules.Rule, error)

CreateRules inserts rules in one transaction and returns them as saved, in the order given. Each lands at its position in the priority order, or at the end; the priorities of all the user's rules are then renumbered from 1, which is not an edit, so versions stay. The caller validates the rules first.

func (*Store) CreateSession

func (s *Store) CreateSession(ctx context.Context, tokenHash string, userID, now, expiresAt int64) error

CreateSession stores a login session by the hash of its token, and sweeps expired ones.

func (*Store) DeleteAccount

func (s *Store) DeleteAccount(ctx context.Context, id, now int64) ([]rules.Rule, error)

DeleteAccount wipes an account with its secret, folders, contacts, messages, decisions, actions and corrections: each references the account with ON DELETE CASCADE. The rules are kept: first, in the same transaction, every user's rules that name the mailbox are rewritten as rules.Rule.DropAccount has it, so a rule limited to it is switched off and marked, with no mailbox, before the cascade could reach it. It returns those rules.

func (*Store) DeleteRule

func (s *Store) DeleteRule(ctx context.Context, userID, id int64) error

DeleteRule removes a rule. The schema takes the sender rules that route to it along (ON DELETE CASCADE) and keeps its decisions and corrections, with their rule set to NULL.

func (*Store) DeleteSenderRule

func (s *Store) DeleteSenderRule(ctx context.Context, userID int64, matchType, value string) error

DeleteSenderRule removes a sender's verdict; deleting a missing one is not an error.

func (*Store) DeleteSession

func (s *Store) DeleteSession(ctx context.Context, tokenHash string) error

DeleteSession ends a session; deleting a missing one is not an error.

func (*Store) DoneActionsSince

func (s *Store) DoneActionsSince(ctx context.Context, ruleID, since int64) ([]Action, error)

DoneActionsSince lists the actions made at or after since that are still in effect, newest first: the ones "undo everything since" reverses. With ruleID, only those a decision for that rule led to.

func (*Store) DryRun

func (s *Store) DryRun(ctx context.Context, def bool) (bool, error)

DryRun reports whether the global dry-run switch is on. def is used until the switch has been set (MAILRULES_DRY_RUN).

func (*Store) DueMessages

func (s *Store) DueMessages(ctx context.Context, accountID, now int64) ([]Message, error)

DueMessages lists an account's failed messages whose retry time has come, oldest first.

func (*Store) ExtendSession

func (s *Store) ExtendSession(ctx context.Context, tokenHash string, expiresAt int64) error

ExtendSession slides a session's expiry forward.

func (*Store) FailRunningBatches

func (s *Store) FailRunningBatches(ctx context.Context) error

FailRunningBatches marks every batch still running as failed. The daemon calls it at startup: a batch that was running when the daemon stopped will never finish.

func (*Store) FinishAction

func (s *Store) FinishAction(ctx context.Context, a Action) error

FinishAction stores how an action ended. For a done action, after is the message's state afterwards, and the message's location is updated with it in the same transaction.

func (*Store) FinishSummary

func (s *Store) FinishSummary(ctx context.Context, id int64, sent bool, periodStart, periodEnd, now int64) error

FinishSummary records how a claimed attempt ended: sent, with the time it covered, or failed.

func (*Store) FirstUser

func (s *Store) FirstUser(ctx context.Context) (User, error)

FirstUser returns the admin account, or ErrNotFound before first-run setup.

func (*Store) Folder

func (s *Store) Folder(ctx context.Context, accountID int64, name string) (Folder, error)

Folder returns one folder, or ErrNotFound.

func (*Store) Folders

func (s *Store) Folders(ctx context.Context, accountID int64) ([]Folder, error)

Folders lists an account's folders by name.

func (*Store) HasUsers

func (s *Store) HasUsers(ctx context.Context) (bool, error)

HasUsers reports whether first-run setup has been done.

func (*Store) HitSenderRule

func (s *Store) HitSenderRule(ctx context.Context, id int64) error

HitSenderRule counts one more email settled by a sender rule.

func (*Store) ImportRules

func (s *Store) ImportRules(ctx context.Context, userID int64, imported []rules.Rule, now int64) (created, updated int, err error)

ImportRules stores the rules of a YAML file in one transaction. A rule whose name is already in use replaces that rule, including its mailbox (AccountID, which the caller has set to the one to keep when the file says nothing), and keeps its place and id; the others are added after the existing rules, in file order. Rules the file does not name are left alone. The caller validates the rules first.

func (*Store) IngestMessage

func (s *Store) IngestMessage(ctx context.Context, ref mail.MsgRef, now int64) (m Message, fresh bool, err error)

IngestMessage records that a message arrived, once. fresh is false when the row was already there (the watcher repeats a message after a crash); the stored row is returned either way. A known message that the executor itself put at ref (an undo back into the watched folder gets a new UID) is not new mail either: its existing row is returned.

func (*Store) InsertAction

func (s *Store) InsertAction(ctx context.Context, a Action) (int64, error)

InsertAction writes an action row and returns its id.

func (*Store) IsContact

func (s *Store) IsContact(ctx context.Context, accountID int64, address string) (bool, error)

IsContact reports whether the account's owner has sent mail to address.

func (*Store) LastMailAt

func (s *Store) LastMailAt(ctx context.Context, accountID int64) (int64, error)

LastMailAt is when the newest email MailRules has seen in an account arrived (its received date, or when it was first seen if it carried none); 0 when it has seen none.

func (*Store) LastSentSummary

func (s *Store) LastSentSummary(ctx context.Context, userID int64) (Summary, error)

LastSentSummary returns the user's latest summary that went out, or ErrNotFound.

func (*Store) LeaveOwnMail

func (s *Store) LeaveOwnMail(ctx context.Context) (bool, error)

LeaveOwnMail reports whether mail from the mailbox's own address is left alone. The pipeline reads it for every email.

func (*Store) LiveBatch

func (s *Store) LiveBatch(ctx context.Context, now time.Time) (int64, error)

LiveBatch returns the batch that collects the actions of live processing for now's day (UTC), creating it on the day's first action, so "undo today" is one batch undo. Once a day's batch has been undone, later mail that day starts a new one.

func (*Store) MarkActionUndone

func (s *Store) MarkActionUndone(ctx context.Context, a Action, restored mail.MsgRef, now int64) error

MarkActionUndone marks a done action undone and records where its message is now.

func (*Store) Message

func (s *Store) Message(ctx context.Context, id int64) (Message, error)

Message returns one message, or ErrNotFound.

func (*Store) MessageActions

func (s *Store) MessageActions(ctx context.Context, messageID int64) ([]Action, error)

MessageActions lists every action taken on a message, oldest first.

func (*Store) MessageByMessageID

func (s *Store) MessageByMessageID(ctx context.Context, accountID int64, messageID string, except int64) (Message, error)

MessageByMessageID returns the account's newest row, other than except, for the email with this Message-ID header, or ErrNotFound.

func (*Store) MessageCorrections

func (s *Store) MessageCorrections(ctx context.Context, messageID int64) ([]Correction, error)

MessageCorrections lists the corrections the user made to a message, oldest first.

func (*Store) MessageDecisions

func (s *Store) MessageDecisions(ctx context.Context, messageID int64) ([]Decision, error)

MessageDecisions lists every decision made for a message, oldest first. A message has more than one when the retry job ran it again.

func (*Store) Ping

func (s *Store) Ping(ctx context.Context) error

Ping reports whether the database answers.

func (*Store) PutSenderRule

func (s *Store) PutSenderRule(ctx context.Context, sr rules.SenderRule, now int64) (rules.SenderRule, error)

PutSenderRule stores the verdict for a sender address or domain, replacing any earlier one for the same sender, and returns it with its id.

func (*Store) QuietRules

func (s *Store) QuietRules(ctx context.Context, userID, since int64) (int, error)

QuietRules counts the user's enabled rules that were applied to no email since `since`.

func (*Store) Rebind

func (s *Store) Rebind(ctx context.Context, fresh, known int64, ref mail.MsgRef) (bool, error)

Rebind records that the email of row known is now at ref, where it was just ingested as row fresh, and removes fresh, so the email keeps one row. fresh must be a row MailRules has done nothing with yet: when it has a decision, an action or a correction, Rebind changes nothing and reports false.

func (*Store) RecentSenderDecisions

func (s *Store) RecentSenderDecisions(ctx context.Context, userID int64, address string, n int) ([]SenderDecision, error)

RecentSenderDecisions returns the decisions of the newest n decided emails from one address across the user's accounts, newest first. An email decided more than once (a retry, a cleanup run) counts once, by its latest decision. A decision whose actions were recorded in dry-run is left out, so a dry-run period teaches the learner nothing, then or once MailRules is live.

func (*Store) ReconcileRemovedMailboxes added in v0.1.3

func (s *Store) ReconcileRemovedMailboxes(ctx context.Context, now int64) ([]rules.Rule, error)

ReconcileRemovedMailboxes fixes the rules that still name a mailbox removed before DeleteAccount kept them (MAI-132): every mailbox a rule names that has no account any more is dropped from it as DeleteAccount would have, for every user, in one transaction. It returns the rules it changed; a second run changes nothing.

func (*Store) ReorderRules

func (s *Store) ReorderRules(ctx context.Context, userID int64, ids []int64) error

ReorderRules sets the rules' priorities to the order of ids, in one transaction. A reorder is not an edit, so versions stay as they are.

func (*Store) Retain

func (s *Store) Retain(ctx context.Context, now int64, retentionDays int) (blanked, deleted int64, err error)

Retain is the retention job's one step: it blanks the snippet of every message first seen more than retentionDays ago, and deletes the messages first seen more than KeepMessagesDays ago that have no action left to undo, that is, none in effect from the last UndoDays (their decisions, actions and corrections go with them). It returns how many rows each part touched.

func (*Store) Rule

func (s *Store) Rule(ctx context.Context, userID, id int64) (rules.Rule, error)

Rule returns one of the user's rules, or ErrNotFound.

func (*Store) RuleCosts

func (s *Store) RuleCosts(ctx context.Context, userID, since int64) ([]RuleUse, error)

RuleCosts lists, per rule, the emails decided for it since `since` and what the model decisions among them cost, dearest first. Every decision counts here, a retry too: it was paid for.

func (*Store) RuleStats

func (s *Store) RuleStats(ctx context.Context, userID, since int64) (map[int64]RuleStat, error)

RuleStats counts, per rule, the messages it was applied to since `since`, with the time of the latest one ever. Mail waiting in Needs review does not count: nothing was applied.

func (*Store) Rules

func (s *Store) Rules(ctx context.Context, userID int64) ([]rules.Rule, error)

Rules returns all of the user's rules, disabled ones included, in priority order.

func (*Store) SaveFolders

func (s *Store) SaveFolders(ctx context.Context, accountID int64, folders []Folder) error

SaveFolders replaces an account's folder list with what discovery found. Watch positions of folders that still exist are kept; folders that are gone are removed.

func (*Store) SaveMessageSummary

func (s *Store) SaveMessageSummary(ctx context.Context, m Message) error

SaveMessageSummary stores the parsed header fields, snippet and signals of a message.

func (*Store) SenderRules

func (s *Store) SenderRules(ctx context.Context, userID int64) ([]rules.SenderRule, error)

SenderRules returns all of the user's sender rules.

func (*Store) SendersSeen

func (s *Store) SendersSeen(ctx context.Context, userID, since int64) ([]SenderSeen, error)

SendersSeen lists every address the user's accounts have seen mail from since `since` (by when MailRules first saw the email).

func (*Store) SessionUser

func (s *Store) SessionUser(ctx context.Context, tokenHash string, now int64) (User, int64, error)

SessionUser returns the user behind a live session and when it expires.

func (*Store) SetAccountCapabilities

func (s *Store) SetAccountCapabilities(ctx context.Context, id int64, capabilities []string) error

SetAccountCapabilities stores what the server advertised at the last login.

func (*Store) SetAccountSecret

func (s *Store) SetAccountSecret(ctx context.Context, master []byte, id int64, secret string) error

SetAccountSecret replaces an account's app password, sealed under a fresh data key.

func (*Store) SetAccountStatus

func (s *Store) SetAccountStatus(ctx context.Context, id int64, status, lastError string, now int64) error

SetAccountStatus records the connection state; lastError is cleared by passing "".

func (*Store) SetBatchProgress

func (s *Store) SetBatchProgress(ctx context.Context, id int64, total, done int) error

SetBatchProgress records how far a batch has come.

func (*Store) SetBatchStatus

func (s *Store) SetBatchStatus(ctx context.Context, id int64, status string) error

SetBatchStatus moves a batch to a status.

func (*Store) SetDryRun

func (s *Store) SetDryRun(ctx context.Context, on bool) error

SetDryRun turns the global dry-run switch on or off. A running daemon reads it before every action, so it takes effect at once.

func (*Store) SetFolderPosition

func (s *Store) SetFolderPosition(ctx context.Context, accountID int64, name string, uidValidity, lastUID uint32) error

SetFolderPosition records how far a watched folder has been processed.

func (*Store) SetMessageState

func (s *Store) SetMessageState(ctx context.Context, id int64, state string, attempts int, nextAttemptAt int64) error

SetMessageState moves a message to a state. nextAttemptAt is when the retry job should run it again; 0 means it is not waiting.

func (*Store) SetPassword added in v0.1.2

func (s *Store) SetPassword(ctx context.Context, userID int64, passwordHash string) error

SetPassword replaces a user's password hash and, in the same transaction, ends every session the user has: a session someone else holds does not outlive the password that opened it. A missing user is ErrNotFound.

func (*Store) SetSetting

func (s *Store) SetSetting(ctx context.Context, key, value string) error

SetSetting stores a JSON value under key, replacing any previous one.

func (*Store) SetSettings

func (s *Store) SetSettings(ctx context.Context, values map[string]*string) error

SetSettings stores several settings in one transaction, so a change is never half applied. A nil value removes the key, which puts its default back in force.

func (*Store) SetTrashToFolder

func (s *Store) SetTrashToFolder(ctx context.Context, on bool) error

SetTrashToFolder turns the trash_to_folder switch on or off.

func (*Store) Setting

func (s *Store) Setting(ctx context.Context, key string) (string, error)

Setting returns a stored JSON value, or ErrNotFound.

func (*Store) Settings

func (s *Store) Settings(ctx context.Context) (map[string]string, error)

Settings returns every stored setting, key to JSON value.

func (*Store) StatsTotals

func (s *Store) StatsTotals(ctx context.Context, userID, since int64) (Totals, error)

StatsTotals counts the user's emails decided since `since`.

func (*Store) SummaryCost

func (s *Store) SummaryCost(ctx context.Context, userID, from, to int64) (costUSD float64, calls int, err error)

SummaryCost is what the model decisions made in [from, to) cost, and how many asked a model.

func (*Store) SummaryReview

func (s *Store) SummaryReview(ctx context.Context, userID int64, limit int) ([]SummaryEmail, int, error)

SummaryReview lists the user's emails waiting in Needs review now, newest first, at most limit of them, and how many there are.

func (*Store) SummaryRules

func (s *Store) SummaryRules(ctx context.Context, userID, from, to int64) ([]RuleUse, error)

SummaryRules lists, per rule, the emails a rule was applied to (sorted or trashed, as the Overview counts them) whose latest decision was made in [from, to), most first.

func (*Store) SummaryTrashed

func (s *Store) SummaryTrashed(ctx context.Context, userID, from, to int64, limit int) ([]SummaryEmail, int, error)

SummaryTrashed lists the emails trashed (or, in dry-run, recorded as trashed) in [from, to) and not undone since, newest first, at most limit of them, and how many there are.

func (*Store) TopRules

func (s *Store) TopRules(ctx context.Context, userID, since int64, limit int) ([]RuleUse, error)

TopRules lists the rules applied to the most emails since `since`, most first.

func (*Store) TrashToFolder

func (s *Store) TrashToFolder(ctx context.Context) (bool, error)

TrashToFolder reports whether trashed mail goes to MailRules' own folder rather than the server's Trash. The executor reads it before every trash action.

func (*Store) UpdateAccount

func (s *Store) UpdateAccount(ctx context.Context, a Account) error

UpdateAccount stores an account's label, watch folder and status (the fields the user edits).

func (*Store) UpdateRule

func (s *Store) UpdateRule(ctx context.Context, r rules.Rule, now int64) error

UpdateRule rewrites a rule's editable columns and bumps its version, so past decisions keep pointing at the wording they were made under.

func (*Store) UpsertContacts

func (s *Store) UpsertContacts(ctx context.Context, accountID int64, contacts []Contact) error

UpsertContacts adds addresses to an account's contacts, keeping the latest sent time.

func (*Store) Usage

func (s *Store) Usage(ctx context.Context, sinceDay string) ([]UsageRow, error)

Usage lists the cost ledger from sinceDay (YYYY-MM-DD) on, oldest day first.

func (*Store) UserByEmail

func (s *Store) UserByEmail(ctx context.Context, email string) (User, error)

UserByEmail looks an account up by its (case-insensitive) email.

type Summary

type Summary struct {
	ID            int64
	UserID        int64
	DueAt         int64
	Status        string
	Attempts      int
	LastAttemptAt int64
	PeriodStart   int64 // 0 until sent
	PeriodEnd     int64
	SentAt        int64
}

Summary is one row of summaries: a scheduled summary email.

type SummaryEmail

type SummaryEmail struct {
	MessageID int64
	FromName  string
	FromAddr  string
	Subject   string
	Folder    string // a trashed email: the folder the trash moved it to; "" = the server's Trash
	DryRun    bool   // a trashed email: the trash was only recorded, in dry-run
}

SummaryEmail is one email as a summary lists it: who sent it, its subject and, for a trashed one, where it went. Never its text.

type Totals

type Totals struct {
	Processed    int // emails decided
	Sorted       int // of those, a rule or sender rule was applied (or recorded, in dry-run) and not undone since
	WithoutModel int // of those, a sender rule or condition rule acted on, with no model asked; never more than Processed
	Trashed      int // emails with a trash action that is in effect (or recorded, in dry-run)

	// Where the processed emails ended up; the four add up to Processed.
	WentSorted  int // a rule was applied and it did not trash the email
	WentTrash   int // a rule was applied and it trashed the email
	WentReview  int // waiting in Needs review
	WentNowhere int // left in the inbox: no rule matched, it was the mailbox's own mail, it could not be handled, or all that was done to it was undone
}

Totals are the counts of the stats tiles.

type UsageRow

type UsageRow struct {
	Day       string // YYYY-MM-DD, UTC
	Provider  string
	Model     string
	Purpose   string // decide | escalate | compose | test | cleanup | suggest
	Calls     int
	TokensIn  int
	TokensOut int
	CostUSD   float64
}

UsageRow is one row of the cost ledger.

type User

type User struct {
	ID           int64
	Email        string
	PasswordHash string
	CreatedAt    int64
}

User is the admin account.

Jump to

Keyboard shortcuts

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