package main import ( "context" "fmt" "log" "time" "github.com/kami/maven/internal/lexicon" "github.com/kami/maven/internal/phraser" "github.com/kami/maven/internal/router" ) // actionReminder handles router.IntentReminder: parse the time when stage-0 // skipped the extractor, then create the reminder. func (h *reactiveHandler) actionReminder(ctx context.Context, dec router.Decision) string { if !dec.Slots.HasTime { // Stage-0 (reminder-wakeword grammar) skips the extractor, so the // time wasn't parsed. Run the parser as a fallback. if dec.Stage == 0 && h.timeParser != nil { t, ok, err := h.timeParser.Parse(ctx, dec.Utterance, h.now()) // Same gate as the extractor (V-577, V-579, V-610): a request whose // hour was not spoken, or was spoken and not read, gets asked about // and is never completed from the clock. if err == nil && ok && router.ResolvedTheHour(dec.Utterance, t) { dec.Slots.Time = t dec.Slots.HasTime = true } } if !dec.Slots.HasTime { return phraser.Ack(phraser.FailReminderTime, nil) } } // The body is what she says at the hour, so the marker and the time come // out of it: the fire time is already a column, and "напомни" is an // instruction that has been carried out (Vikunja #469). payload := `{"text":` + jsonString(reminderBody(dec.Utterance, dec.Slots.Text)) + `}` if _, err := h.api.CreateReminder(ctx, dec.Slots.Time, payload, ""); err != nil { log.Printf("voice: create reminder: %v", err) return phraser.Ack(phraser.FailReminder, nil) } // Phrased from the row, never from the utterance (Vikunja #507). The // replier only ever saw Slots.Text, so it named whatever hour the sentence // contained — including one the parser had rejected or read differently. // A confirmation naming an hour no row holds is worse than a clarify, // because he stops thinking about it. return reminderConfirm(dec.Slots.Time, h.now()) } // reminderConfirm — the confirmation for a reminder that exists, naming the // stored fire time. Deterministic on purpose: the one sentence that must match // a database row is not one to hand to a 1.7B. func reminderConfirm(fire, now time.Time) string { when := dayPrefix(now, fire) if when == "это" { // Further out than the day words reach — say the date instead of a // word that would be wrong. date := fmt.Sprintf("%d %s", fire.Day(), lexicon.MonthGenitive(int(fire.Month()))) return "хорошо, напомню " + date + " в " + fire.Format("15:04") + "." } return "хорошо, напомню " + when + " в " + fire.Format("15:04") + "." }