CI / ci (push) Successful in 8m28s
Co-authored-by: dev_agent <dev_agent@cubecraftlabs.com>
134 lines
4.9 KiB
Go
134 lines
4.9 KiB
Go
package shopping
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/household"
|
|
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/notify"
|
|
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/scheduler"
|
|
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/store"
|
|
)
|
|
|
|
// RolloverTime is when, every Monday in the household zone, week_rollover
|
|
// freezes the week that has just ended. It is fixed, not a setting.
|
|
const RolloverTime = "00:05"
|
|
|
|
// Reasons Autosend posts nothing.
|
|
const (
|
|
AutosendNotConfigured = "no Google Chat webhook is configured"
|
|
AutosendChatOff = "list_ready is switched off for Google Chat"
|
|
AutosendNoList = "the week has no Supply run"
|
|
AutosendAlreadySent = "the Supply run was already sent to Google Chat"
|
|
AutosendUnconfirmed = "a send of the Supply run to Google Chat is unconfirmed"
|
|
)
|
|
|
|
// AutosendKey identifies the automatic send of the Supply run of the week
|
|
// starting weekStart, list_ready:chat:YYYY-MM-DD: it is the
|
|
// shopping_chat_autosend period key claimed in schedule_runs. The send
|
|
// itself is recorded in the list's sent_to_chat_at.
|
|
func AutosendKey(weekStart store.LocalDate) string {
|
|
return string(notify.ListReady) + ":" + string(notify.ChannelChat) + ":" + weekStart.String()
|
|
}
|
|
|
|
// ChatAutosendSchedule is the shopping_chat_autosend schedule: at the
|
|
// household's chat_autosend_dow and chat_autosend_time it sends the Supply
|
|
// run of the coming week, the week after the fire date's, to Google Chat
|
|
// with Autosend. Its period is that week, scheduler.WeeklyForNextWeek's,
|
|
// claimed under AutosendKey of the week's Monday. A send that fails is the
|
|
// period's error and is not retried; the household can still send it from
|
|
// the Supply run.
|
|
func (s *Service) ChatAutosendSchedule() scheduler.Schedule {
|
|
return scheduler.Schedule{
|
|
Name: scheduler.ShoppingChatAutosend,
|
|
Rule: func(settings household.Settings) scheduler.Rule {
|
|
return scheduler.WeeklyForNextWeek(time.Weekday(settings.ChatAutosendDow), settings.ChatAutosendTime)
|
|
},
|
|
PeriodKey: func(p scheduler.Period) string {
|
|
return AutosendKey(p.Week)
|
|
},
|
|
Run: func(ctx context.Context, p scheduler.Period) error {
|
|
skipped, err := s.Autosend(ctx, p.Week)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
log := s.logger.With("period", p.Key, "week_start", p.Week.String())
|
|
if skipped != "" {
|
|
log.InfoContext(ctx, "Supply run not sent to Google Chat: "+skipped)
|
|
return nil
|
|
}
|
|
log.InfoContext(ctx, "Supply run sent to Google Chat")
|
|
return nil
|
|
},
|
|
}
|
|
}
|
|
|
|
// Autosend sends the Supply run of the week starting weekStart to Google
|
|
// Chat with SendToChat, as the household's send does, when a webhook is
|
|
// stored, list_ready's Google Chat channel is switched on, and the week has
|
|
// a Supply run not yet sent and with no send unconfirmed. Otherwise it
|
|
// posts nothing and returns which of those reasons stopped it; it never
|
|
// resends. A send that fails is SendToChat's error.
|
|
func (s *Service) Autosend(ctx context.Context, weekStart store.LocalDate) (skipped string, err error) {
|
|
settings, err := s.chat.Get(ctx)
|
|
if err != nil {
|
|
return "", fmt.Errorf("shopping: autosend: %w", err)
|
|
}
|
|
if settings.URLMasked == nil {
|
|
return AutosendNotConfigured, nil
|
|
}
|
|
on, err := s.notifier.ChannelOn(ctx, notify.ListReady, notify.ChannelChat)
|
|
if err != nil {
|
|
return "", fmt.Errorf("shopping: autosend: %w", err)
|
|
}
|
|
if !on {
|
|
return AutosendChatOff, nil
|
|
}
|
|
l, err := s.Get(ctx, weekStart)
|
|
if errors.Is(err, ErrListNotFound) {
|
|
return AutosendNoList, nil
|
|
}
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
switch {
|
|
case l.SentToChatAt != nil:
|
|
return AutosendAlreadySent, nil
|
|
case l.ChatSendUnconfirmedAt != nil:
|
|
return AutosendUnconfirmed, nil
|
|
}
|
|
if _, err := s.SendToChat(ctx, weekStart, false); err != nil {
|
|
return "", err
|
|
}
|
|
return "", nil
|
|
}
|
|
|
|
// RolloverSchedule is the week_rollover schedule: every Monday at
|
|
// RolloverTime it freezes the Supply run of the week before the fire
|
|
// date's into the Supply archive with Freeze, the week ending by the fire
|
|
// time. Its period scheduler.Weekly keys week_rollover:YYYY-Www for the
|
|
// fire date's week. A week with no Supply run has nothing to freeze, and
|
|
// the period says so; a freeze that fails is the period's error and is not
|
|
// retried.
|
|
func (s *Service) RolloverSchedule() scheduler.Schedule {
|
|
return scheduler.Schedule{
|
|
Name: scheduler.WeekRollover,
|
|
Rule: func(household.Settings) scheduler.Rule {
|
|
return scheduler.Weekly(time.Monday, RolloverTime)
|
|
},
|
|
Run: func(ctx context.Context, p scheduler.Period) error {
|
|
y, m, d := p.Week.Date()
|
|
last := store.LocalDateOf(time.Date(y, m, d-7, 0, 0, 0, 0, time.UTC))
|
|
err := s.freeze(ctx, last, p.FireAt)
|
|
if errors.Is(err, ErrListNotFound) {
|
|
s.logger.InfoContext(ctx, "week rollover froze nothing: the week has no Supply run",
|
|
"period", p.Key, "week_start", last.String())
|
|
return nil
|
|
}
|
|
return err
|
|
},
|
|
}
|
|
}
|