119 lines
3.9 KiB
Go
119 lines
3.9 KiB
Go
package main
|
|
|
|
// Sending the mail signal (novox/hq ADR 0461 §1, §2, §4 to §6; issue 1522): the words (signal.go, issue 1429)
|
|
// checked with messenger.messenger_check, then one seat:operator-channel.tell with subject "mail" — or the
|
|
// safe form when the words are refused. mesh-mail-reader's manifest invokes both (1522/AC4).
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
)
|
|
|
|
// MessengerCheck is one seat:messenger.messenger_check ask (ADR 0461 §4): whether text is safe to say on an
|
|
// operator channel.
|
|
type MessengerCheck func(text string) (allowed bool, err error)
|
|
|
|
// NewMessengerCheck is MessengerCheck over the module's own Ask (batch.go).
|
|
func NewMessengerCheck(ask Ask) MessengerCheck {
|
|
return func(text string) (bool, error) {
|
|
raw, err := ask("messenger.messenger_check", map[string]any{"text": text})
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
var out struct {
|
|
Allowed bool `json:"allowed"`
|
|
}
|
|
if err := json.Unmarshal(raw, &out); err != nil {
|
|
return false, fmt.Errorf("messenger_check's answer could not be read: %.200s", raw)
|
|
}
|
|
return out.Allowed, nil
|
|
}
|
|
}
|
|
|
|
// Tell is one seat:operator-channel.tell ask (ADR 0461 §1): the headline, at most two lines, and the
|
|
// subject "mail" (1522/AC1). SentOn answers non-empty once a channel actually said it (1522/AC2); empty
|
|
// means every channel dropped it (the cap of 10 an hour, messenger's outbox.go) or refused it outright.
|
|
type Tell func(headline string, lines []string, subject string) (sentOn string, err error)
|
|
|
|
// NewTell is Tell over the module's own Ask (batch.go).
|
|
func NewTell(ask Ask) Tell {
|
|
return func(headline string, lines []string, subject string) (string, error) {
|
|
raw, err := ask("seat:operator-channel.tell", map[string]any{"headline": headline, "lines": lines, "subject": subject})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
var out struct {
|
|
SentOn string `json:"sent_on"`
|
|
}
|
|
if err := json.Unmarshal(raw, &out); err != nil {
|
|
return "", fmt.Errorf("operator-channel.tell's answer could not be read: %.200s", raw)
|
|
}
|
|
return out.SentOn, nil
|
|
}
|
|
}
|
|
|
|
// SendSignal is 1522: every entry marked matters and not yet told — including one carried over from an
|
|
// earlier batch that went untold (1429/AC4) — is gathered; its words are built (signal.go) and checked word
|
|
// by word with check; any one refused falls back to the safe form (1522/AC1). Nothing is sent, and nothing
|
|
// asked at all, when there is nothing to say (a batch whose only mark is scam warning, 1522/AC3) or while
|
|
// the reader is unhealthy (1522/AC3, ADR 0461 §6) — read fresh from the store, not assumed from the caller's
|
|
// own last outcome. The tell sent, every one of those entries is marked told only once it answers sent_on
|
|
// (1522/AC2); a tell that answers sent_on empty leaves them exactly as they were, for the next signal to
|
|
// count (1522/AC2, AC3).
|
|
func SendSignal(ctx context.Context, store Store, check MessengerCheck, tell Tell) error {
|
|
health, err := store.ReaderHealth(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !health.Healthy {
|
|
return nil
|
|
}
|
|
entries, err := store.Entries(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var toTell []Entry
|
|
var mails []SignalMail
|
|
for _, e := range entries {
|
|
if e.Mark == MarkMatters && !e.Told {
|
|
toTell = append(toTell, e)
|
|
mails = append(mails, SignalMail{SenderName: e.SenderName, Summary: e.Summary, Date: e.Date, DueDate: e.DueDate})
|
|
}
|
|
}
|
|
if len(toTell) == 0 {
|
|
return nil
|
|
}
|
|
|
|
words := BuildSignal(mails)
|
|
allowed := true
|
|
for _, line := range append([]string{words.Headline}, words.Lines...) {
|
|
ok, err := check(line)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !ok {
|
|
allowed = false
|
|
break
|
|
}
|
|
}
|
|
if !allowed {
|
|
words = SafeSignal(len(toTell))
|
|
}
|
|
|
|
sentOn, err := tell(words.Headline, words.Lines, "mail")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if sentOn == "" {
|
|
return nil
|
|
}
|
|
for _, e := range toTell {
|
|
e.Told = true
|
|
if err := store.UpdateEntry(ctx, e); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|