Documentation
¶
Overview ¶
Package r2arrival sends the one "Your R2 may be here" email.
A user who is waiting for an R2 may tell Rivolt the month they expect it (a month, never a day; engagement_prefs.r2_arrival_month, off until chosen). On the first weekday of that month, at 08:00 in their time zone, they get one email with the connect steps. It goes only to a verified address, only while no vehicle is connected, only when the user has not turned engagement emails off, counts against the engagement budget, and is claimed in engagement_sends under the month, so it is sent at most once across replicas and restarts. It is never sent after its month, and never a second time. Once its email is claimed (sent, failed or already sent) the month is deleted, and a month that has ended is deleted by the sweep: the due list is capped, so finished rows must not crowd later users out, and the month is not kept past its purpose.
Index ¶
Constants ¶
const ( SendHour = 8 FallbackUTCHour = 14 )
SendHour is the local hour of the send. A user whose browser never stored a time zone gets the same message at FallbackUTCHour, the morning in the Americas, where the first R2s are.
const ( OutcomeNoMailer = "no_mailer" OutcomeNoSigner = "no_signer" )
Outcomes beyond Engage's own, as the job's callback names them.
const Kind = "r2_arrival"
Kind is the engagement_sends kind.
const UnsubPurpose = "r2-arrival"
UnsubPurpose scopes the signed one-click link in the email to this reminder alone.
Variables ¶
This section is empty.
Functions ¶
func BuildMessage ¶
BuildMessage is the email (DA-approved wording, Oct 31): the connect steps for an R2, how to change or delete the reminder, and a one-click link that deletes it.
Types ¶
type Job ¶
type Job struct {
Source Source
Engage *engage.Engage
Mail Mailer
// BaseURL is the public origin for the links in the email.
BaseURL string
// Signer signs the one-click link. Nil, or a nil result: nothing is sent (no way to opt out, no mail).
Signer func() *statement.Signer
// Skip excludes a user (the demo account).
Skip func(uid uuid.UUID) bool
Now func() time.Time
Log *slog.Logger
// OnOutcome is told each send attempt's outcome (engage's, or OutcomeNoSigner).
OnOutcome func(outcome string)
}
Job is the sweep. Set Source, Engage and Mail; Mail nil counts nothing and sends nothing.
type Source ¶
type Source interface {
Due(ctx context.Context, from, to time.Time) ([]db.R2ArrivalDue, error)
StillWanted(ctx context.Context, uid uuid.UUID, month time.Time) (bool, error)
// Clear deletes the user's month if it is still this one; ClearEnded deletes every month that has ended.
Clear(ctx context.Context, uid uuid.UUID, month time.Time) error
ClearEnded(ctx context.Context, now time.Time) (int64, error)
}
Source is the database side.