Documentation
¶
Overview ¶
Package rules provides a CEL-based message rules engine for the mailbox library.
Rules evaluate Common Expression Language (CEL) conditions against incoming or outgoing messages and apply actions such as moving to a folder, adding tags, marking as read, or suppressing delivery.
The engine is designed as a mailbox Plugin (implementing SendHook for send-side rules) and an event handler (OnMessageReceived for receive-side rules). This keeps the core mailbox library clean and makes rules fully optional.
Rule Scopes ¶
Rules have two scopes:
- ScopeReceive: evaluated when a message is delivered to a recipient. All actions are valid (SetFolder, AddTag, RemoveTag, MarkRead, Delete, HardDelete, Archive, Spam, Webhook, Forward).
- ScopeSend: evaluated after the sender's copy is created. Only SetFolder, AddTag, RemoveTag, Archive, and Webhook are valid; other actions are silently skipped.
CEL Variables ¶
The following variables are available in CEL expressions:
sender string - sender user ID subject string - message subject body string - message body recipients list(string) - recipient user IDs headers map(string, string) - message headers metadata map(string, dyn) - message metadata has_attachments bool - whether the message has attachments attachment_count int - number of attachments thread_id string - thread ID (empty if not a reply) is_reply bool - true if the message is a reply folder string - current folder ID tags list(string) - current tags
Rule Evaluation ¶
All matching rules are applied in priority order (lower value = higher priority). Actions accumulate: if multiple rules set the folder, the last one wins. A rule with StopOnMatch=true stops further rule evaluation for that message.
Integration ¶
Wire the engine as a plugin and event subscriber:
engine, err := rules.NewEngine(provider, st)
svc, err := mailbox.New(mailbox.Config{},
mailbox.WithStore(st),
mailbox.WithPlugin(engine), // registers AfterSend for send-side rules
)
svc.Connect(ctx)
// Subscribe receive-side rules via an adapter (rules can't import mailbox directly):
svc.Events().MessageReceived.Subscribe(ctx,
func(ctx context.Context, ev event.Event[mailbox.MessageReceivedEvent], data mailbox.MessageReceivedEvent) error {
return engine.OnMessageReceived(ctx, nil, rules.MessageReceivedData{
MessageID: data.MessageID,
RecipientID: data.RecipientID,
})
},
event.AsWorker("rules-engine"),
)
Index ¶
- Constants
- type Action
- type ActionType
- type Engine
- func (e *Engine) AfterSend(ctx context.Context, userID string, msg store.Message) error
- func (e *Engine) BeforeSend(_ context.Context, _ string, _ store.DraftMessage) error
- func (e *Engine) Close(_ context.Context) error
- func (e *Engine) Init(_ context.Context) error
- func (e *Engine) Name() string
- func (e *Engine) OnMessageReceived(ctx context.Context, _ any, data MessageReceivedData) error
- type Forwarder
- type MessageReceivedData
- type Option
- type Rule
- type RuleProvider
- type RuleScope
- type StaticRuleProvider
Constants ¶
const DefaultMaxCacheSize = 1000
DefaultMaxCacheSize is the default maximum number of compiled CEL programs to cache.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Action ¶
type Action struct {
// Type is the action to perform.
Type ActionType
// Value is the action parameter. Semantics depend on Type:
// SetFolder — destination folder ID
// AddTag — tag ID to add
// RemoveTag — tag ID to remove
// Webhook — URL to POST the message payload to
// Forward — target user ID to forward the message to
// All others — ignored
Value string
}
Action defines what to do when a rule matches.
type ActionType ¶
type ActionType string
ActionType defines the type of action to perform when a rule matches.
const ( // ActionSetFolder moves the message to a specific folder. ActionSetFolder ActionType = "set_folder" // ActionAddTag adds a tag to the message. ActionAddTag ActionType = "add_tag" // ActionRemoveTag removes a tag from the message. Value is the tag ID. ActionRemoveTag ActionType = "remove_tag" // ActionMarkRead marks the message as read. Only valid for receive scope. ActionMarkRead ActionType = "mark_read" // ActionDelete soft-deletes the message (moves to trash). Only valid for receive scope. ActionDelete ActionType = "delete" // ActionHardDelete permanently removes the message. Only valid for receive scope. // Use with caution -- hard-deleted messages cannot be recovered. ActionHardDelete ActionType = "hard_delete" // ActionArchive moves the message to the archived folder. ActionArchive ActionType = "archive" // ActionSpam moves the message to the spam folder. Only valid for receive scope. ActionSpam ActionType = "spam" // ActionWebhook POSTs the message payload as JSON to the URL in Value. // Delivery is fire-and-forget; errors are logged but do not abort rule processing. ActionWebhook ActionType = "webhook" // ActionForward forwards the message to another user. Value is the target userID. // Requires a Forwarder configured via WithForwarder. ActionForward ActionType = "forward" )
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine evaluates CEL rules and applies actions via store operations. It implements the mailbox.Plugin and mailbox.SendHook interfaces.
func NewEngine ¶
NewEngine creates a rules engine with the given provider and store. The store is used to apply post-creation mutations (MoveToFolder, AddTag, etc.).
func (*Engine) AfterSend ¶
AfterSend evaluates send-scope rules against the sender's message copy and applies valid send-side actions (SetFolder, AddTag, Archive).
func (*Engine) BeforeSend ¶
BeforeSend is a no-op. Rules do not block message sending.
func (*Engine) Init ¶
Init initializes the plugin. The CEL environment is created in the constructor.
func (*Engine) OnMessageReceived ¶
OnMessageReceived handles MessageReceived events and evaluates receive-scope rules. Register this as an event handler on the service's MessageReceived event.
The handler signature matches event.Handler[mailbox.MessageReceivedEvent]. The second parameter is the event instance; it is unused here.
type Forwarder ¶ added in v0.7.6
type Forwarder interface {
// Forward delivers a copy of messageID to toUserID.
Forward(ctx context.Context, messageID, toUserID string) error
}
Forwarder forwards a message to another user on behalf of the rules engine. Implement this interface and register it via WithForwarder to enable ActionForward.
type MessageReceivedData ¶
MessageReceivedData contains the fields needed by OnMessageReceived. This matches the shape of mailbox.MessageReceivedEvent without importing the parent package (avoiding a circular dependency).
type Option ¶
type Option func(*engineOptions)
Option configures the rules Engine.
func WithForwarder ¶ added in v0.7.6
WithForwarder sets the forwarder used by ActionForward rules. Without a forwarder, ActionForward is logged and skipped.
func WithHTTPClient ¶ added in v0.7.6
WithHTTPClient sets the HTTP client used by ActionWebhook rules. Defaults to http.DefaultClient when not set.
func WithLogger ¶
WithLogger sets the logger for the rules engine.
func WithMaxCacheSize ¶
WithMaxCacheSize sets the maximum number of compiled CEL programs to cache. When the cache is full, all entries are evicted. Defaults to DefaultMaxCacheSize (1000). Set to 0 to disable caching.
func WithStrictMode ¶
WithStrictMode makes rule evaluation errors fatal. When enabled, CEL compilation or evaluation errors cause the operation to fail instead of being logged and skipped.
type Rule ¶
type Rule struct {
// ID uniquely identifies the rule.
ID string
// Name is a human-readable label for the rule.
Name string
// Scope determines when the rule is evaluated (receive or send).
// Defaults to ScopeReceive if empty.
Scope RuleScope
// Condition is a CEL expression that must evaluate to true for actions to apply.
Condition string
// Actions are applied in order when the condition matches.
Actions []Action
// Priority controls evaluation order. Lower values are evaluated first.
Priority int
// StopOnMatch stops evaluating further rules for this message when true.
StopOnMatch bool
}
Rule defines a single rule with a CEL condition and actions.
type RuleProvider ¶
type RuleProvider interface {
// GlobalRules returns rules that apply to all users for the given scope.
GlobalRules(ctx context.Context, scope RuleScope) ([]Rule, error)
// UserRules returns rules for a specific user and scope.
UserRules(ctx context.Context, userID string, scope RuleScope) ([]Rule, error)
}
RuleProvider supplies rules for evaluation. Implementations can be backed by a database, config file, or any other source.
type StaticRuleProvider ¶
type StaticRuleProvider struct {
// contains filtered or unexported fields
}
StaticRuleProvider is an in-memory RuleProvider for testing and simple use cases.
func NewStaticProvider ¶
func NewStaticProvider(global []Rule, perUser map[string][]Rule) *StaticRuleProvider
NewStaticProvider creates a StaticRuleProvider with the given global and per-user rules.
func (*StaticRuleProvider) GlobalRules ¶
GlobalRules returns global rules filtered by scope.