Documentation
¶
Overview ¶
Package rules holds the rule types, the condition matcher, validation, the evaluation order for one email and YAML import/export. It never calls a model and never touches a mailbox.
Index ¶
Constants ¶
const ( OpEq = "eq" OpNe = "ne" OpIn = "in" OpContains = "contains" OpContainsAny = "contains_any" OpNotContains = "not_contains" OpMatches = "matches" OpGt = "gt" OpLt = "lt" OpExists = "exists" )
Condition operators.
const ( MatchAddress = "address" MatchDomain = "domain" VerdictRoute = "route" VerdictKeep = "keep" VerdictBlock = "block" VerdictMove = "move" )
Sender rule match types and verdicts.
const ( ActMove = "move" ActArchive = "archive" ActTrash = "trash" ActJunk = "junk" ActFlag = "flag" ActUnflag = "unflag" ActRead = "read" ActUnread = "unread" ActKeep = "keep" )
Action types, in the order the plan lists them.
const AccountByAddress = "" /* 169-byte string literal not displayed */
AccountByAddress is what is wrong with a mailbox number in a rules file's account condition.
const AllMailboxes = "all"
AllMailboxes is the applies_to value of a rule that is not limited to one mailbox. An export never writes it (no applies_to means the rule applies everywhere); an import accepts it to widen a rule that is limited to one.
const MaxDraftInstruction = 300
MaxDraftInstruction caps a rule's draft instruction, in characters.
const MinTrashConfidence = 0.85
MinTrashConfidence is the lowest threshold an intent-based trash rule may have.
Variables ¶
This section is empty.
Functions ¶
func FolderProblem ¶
FolderProblem says, in a sentence, what is wrong with a folder name a rule would move mail to, or "" when it is fine. It is the check a move action's folder gets.
func MarshalYAML ¶
MarshalYAML writes a rules file in priority order, using the shorthands. A rule limited to a mailbox (AccountID set) needs its address in f.Mailboxes, and an account condition its mailboxes' addresses in place of their ids, or the file would not say which they are.
Types ¶
type Cond ¶
type Cond struct {
All []Cond `json:"all,omitempty"`
Any []Cond `json:"any,omitempty"`
Field string `json:"field,omitempty"`
Op string `json:"op,omitempty"`
Value any `json:"value,omitempty"`
// contains filtered or unexported fields
}
Cond is one node of a condition tree: a group ("all" or "any" of its children) or a leaf (field, op, value). The zero value is the empty tree, which always matches.
func (Cond) MapAccounts ¶ added in v0.1.3
MapAccounts returns the tree with fn applied to every mailbox an account condition names (each item of an "in" list on its own) and everything else as it was; c is not changed. The app names a mailbox by its id and a rules file by its address: whoever has the accounts turns one into the other with this.
func (Cond) Match ¶
Match reports whether the email satisfies the tree. now is only used by age_days. A leaf that would not pass validation never matches.
func (Cond) Text ¶
Text renders the tree as one line of plain English, for showing a rule's "unless" part to a model: `from_domain is one of a.com, b.com and (subject contains refund or is_bulk is true)`. The empty tree renders as "".
func (*Cond) UnmarshalJSON ¶
UnmarshalJSON decodes a node strictly (a misspelt key must not turn a leaf into an empty, always-true tree) and compiles its regex once.
type Defaults ¶
type Defaults struct {
DecisionModel string `yaml:"decision_model,omitempty"`
FallbackModel string `yaml:"fallback_model,omitempty"`
MinConfidence *float64 `yaml:"min_confidence,omitempty"`
}
Defaults is the optional "defaults" block. The models are daemon settings, so the importer checks them against the install's (an import never changes a model); MinConfidence is copied onto every rule that sets none.
type Draft ¶ added in v0.3.0
type Draft struct {
// Instruction is how the reply should sound or what it should say ("polite no"); empty =
// the model's own judgement.
Instruction string `json:"instruction" yaml:"instruction,omitempty"`
// ReadPrevious also hands the model the thread's previous message, when it is found.
ReadPrevious bool `json:"read_previous" yaml:"read_previous,omitempty"`
}
Draft is how a rule drafts a reply.
type Evaluation ¶
type Evaluation struct {
Final *Result
Candidates []Rule // passing intent rules above the cut-off, in priority order
CutOff *Rule // the default when the decider picks none; may be nil
// contains filtered or unexported fields
}
Evaluation is what Evaluate found. When Final is set the email is settled without a model. Otherwise the caller asks the decider to pick among Candidates and passes its answer to Resolve.
func Evaluate ¶
func Evaluate(e message.Summary, rs []Rule, senders []SenderRule, opt Options) Evaluation
Evaluate runs the evaluation order for one email: sender rules, then the priority walk down to the cut-off rule. It makes no model call.
func (Evaluation) Resolve ¶
func (ev Evaluation) Resolve(ruleID int64, confidence float64) Result
Resolve turns the decider's answer into the final result (step 5). A pick outside the candidates counts as "none of these", for which the cut-off rule applies; when none does, the result keeps the decider's confidence that no rule applies. A pick below its threshold goes to Needs review with no action at all, even when there is a cut-off rule.
type Field ¶
type Field struct {
Name string
Type string // string | boolean | number | pass, fail or none | account id
Ops []string // the operators it accepts
}
Field describes one condition field for whoever writes conditions: the rule composer's prompt is built from this list.
type File ¶
type File struct {
Defaults Defaults
// Rules are as the file has them: an account condition names each mailbox by its
// address (a string), not by its id, for the same reason as Mailboxes.
Rules []Rule
// Mailboxes says which mailbox each rule applies to, by the rule's name: the mailbox's
// address (its login), or AllMailboxes. A rule with no entry says nothing about it. A
// file cannot hold numeric account ids, which differ from one install to the next, so
// whoever has the accounts maps the address to Rule.AccountID and back.
Mailboxes map[string]string
}
File is a rules YAML document in the PRD shape. The database stays the source of truth; this is import/export only.
type Options ¶
type Options struct {
MinConfidence float64 // threshold for rules that set none
Now time.Time // for age_days
// RouteOnly are rules a sender rule may route to although they take no part in the
// walk: a manual run of only some rules leaves the others out of the walk, yet the
// user's standing sender answers still apply (MAI-43). nil in live sorting.
RouteOnly []Rule
}
Options are the per-run settings Evaluate needs.
type Result ¶
type Result struct {
Stage Stage
RuleID int64 // 0 = no rule; with Review, the rule the decider suggested
RuleVersion int
Confidence float64
Actions []Action // the primary rule's actions, then each stacking rule's
Stacked []int64 // stacking rules whose actions were appended
Review bool // below threshold: take no action, show in Needs review
// SenderRuleID is the sender rule that settled the email (Stage is StageSender), and
// SenderSource who made it (user | learned); 0 and empty otherwise.
SenderRuleID int64
SenderSource string
}
Result is the final outcome for one email.
type Rule ¶
type Rule struct {
ID int64
UserID int64
AccountID int64 // 0 = all accounts
Name string
Said string // the user's original wording; "" when they gave none
Template string // the name of the gallery template it was added from; "" = none
Intent string // plain-English intent for the decider; "" = condition-only
Conditions Cond
Exceptions Cond // "unless"
Actions []Action
Priority int // lower runs first
Stack bool
Model string // per-rule decider override
MinConfidence *float64 // nil = the configured default
Enabled bool
// MailboxRemoved marks a rule whose mailbox was removed (DropAccount): it is off and
// may not be switched on until it is given a mailbox, or All mailboxes, or new
// conditions, which clears the mark.
MailboxRemoved bool
// Notify are the tenant's notification destinations told each time the rule acts on an
// email live (store notify_destinations ids); nil = none. A rules file does not carry
// them: they name this install's destinations.
Notify []int64
// Draft, when set, has a reply drafted each time the rule acts on an email live (a
// module writes it; the daemon saves it in the mailbox's Drafts folder and never sends
// it); nil = none.
Draft *Draft
Version int
CreatedAt int64
UpdatedAt int64
}
Rule mirrors one row of the rules table.
func (Rule) AccountIDs ¶ added in v0.1.3
AccountIDs is every mailbox the rule names, by its mailbox (AccountID) or in an account condition of its conditions or exceptions, without repeats.
func (Rule) DropAccount ¶ added in v0.1.3
DropAccount is the rule as it is to be once mailbox id is removed, and whether that changes it. No mail comes from that mailbox any more, so:
- A rule for that mailbox only is kept, switched off and marked (MailboxRemoved), with no mailbox, so the user can give it another one.
- An account condition naming it is simplified so the rule does on the other mailboxes exactly what it did: "eq" it is false, "ne" it is true, "in" loses it (false when nothing is left), and the constants run up through the groups.
- When the conditions can then never match, or the exceptions always apply, the rule could never act again: it is switched off and marked instead, with the mailbox taken out of its trees so the user still sees the rest of what they wrote. A condition-only rule left with no condition at all is treated the same way, as a rule without conditions or intent is one MailRules refuses to save.
- Exceptions that can no longer apply are dropped.
r is not changed.
type RuleError ¶
type RuleError struct {
N int // the rule's place in the file, from 1
ID string // its id (name) as written
Err *ValidationError
}
RuleError is a problem with one rule of a rules file: which rule, and what is wrong.
type SenderRule ¶
type SenderRule struct {
ID int64
UserID int64
MatchType string // address | domain
Value string // lower-case
RuleID int64 // for route: the rule whose actions apply; 0 otherwise
Verdict string // route | keep | block | move
// Folder is where a move verdict files the sender's mail, on whichever mailbox it
// arrives in; "" otherwise.
Folder string
Source string // user | learned
Hits int
CreatedAt int64
}
SenderRule mirrors one row of the sender_rules table.
type Stage ¶
type Stage string
Stage says which step produced a result; it is stored in decisions.stage. The pipeline rewrites StageDecider to "fallback" when the fallback model answered, which only it knows.
type ValidationError ¶
ValidationError is a rule problem. Path is the offending key as the API reports it, e.g. "conditions.all[0].op". Message is a sentence for a person, complete without the path: capitalised, ending with a full stop, and naming no field path. Error joins the two for a log or the command line.
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string