J.A.R.V.I.S-rust/resources/commands/scheduler/add_recurring.lua
Bossiara13 12b1ed4ccb feat(scheduler): IMBA-5 proactive scheduler — reminders, daily briefings, intervals
Background thread that wakes J.A.R.V.I.S. on a schedule to speak reminders.
Nothing else competes here on desktop — Алиса/Сири are reactive-only.

Core (crates/jarvis-core/src/scheduler.rs)
  - Schedule::{Daily{h,m}, Interval{secs}, Once{at}} with Schedule::parse for
    "daily HH:MM" / "at HH:MM" / "every N minutes|hours" / "in N minutes|hours".
    Russian units (час/часа/часов/минут/секунд) also accepted.
  - ScheduledTask {id, name, schedule, action, last_fired, enabled, created_at}.
    Action::Speak{text} | Action::Lua{script_path}.
  - JSON persistence at <APP_CONFIG_DIR>/schedule.json, atomic write-through.
  - add/remove/clear/list/find; mark_fired auto-deletes Once tasks.
  - start_background() spawns a 30-second tick thread (idempotent). Each tick
    calls due_tasks(), fires Action via tts::speak_default (after voices::play_reply
    "ahem" cue) or Lua engine.
  - 7 unit tests (all passing).

Lua API (crates/jarvis-core/src/lua/api/scheduler.rs)
  - jarvis.scheduler.add({name, schedule, action={type, text|script_path}})
  - jarvis.scheduler.{list, count, remove(id), clear}.
  - Tasks come back as {id, name, schedule_human, action, enabled, last_fired}.

Wire-up (crates/jarvis-app/src/main.rs)
  - scheduler::init() + scheduler::start_background() after profiles::init.
  - Tasks survive restarts via schedule.json.

Voice commands (resources/commands/scheduler/, 6 ids)
  - scheduler.add_reminder    "напомни через 5 минут выключить кофеварку"
  - scheduler.add_at          "напомни в 18:00 забрать ребёнка"
  - scheduler.add_recurring   "каждые 2 часа напоминай попить воды"
  - scheduler.add_daily       "каждый день в 9:00 делай briefing"
  - scheduler.list            "что у меня запланировано"
  - scheduler.clear           "очисти расписание"

Russian-aware parsers (час/часа/часов, минут/минуту/минуты) live inside the Lua
packs — easy to extend without touching Rust.

Tests: 31/31 jarvis-core unit tests pass (24 prior + 7 scheduler).
Build: cargo build --release -p jarvis-app and -p jarvis-gui both green.
2026-05-15 15:43:04 +03:00

74 lines
2.6 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- "каждые 2 часа напоминай попить воды" / "каждый час напоминай размяться"
local phrase = (jarvis.context.phrase or ""):lower()
local body = jarvis.text.strip_trigger(phrase, {
"напоминай каждые",
"напоминай каждый",
"каждые",
"каждый час",
"remind me every",
"every",
"кожні",
"нагадуй кожні",
})
body = body:gsub("^[%s,:%.]+", ""):gsub("%s+$", "")
-- Parse "<N> <unit> <verb> <text>" or "час <text>" (implicit 1)
local n_str, unit, rest = body:match("^(%d+)%s+(%S+)%s*(.*)$")
local n, unit_norm, sample
if n_str then
n = tonumber(n_str)
if unit:match("^минут") or unit:match("^minute") then
unit_norm = "minutes"
sample = n .. " минут"
elseif unit:match("^час") or unit:match("^hour") then
unit_norm = "hours"
sample = n .. " час" .. (n == 1 and "" or (n < 5 and "а" or "ов"))
else
jarvis.speak("Не понял единицу.")
jarvis.audio.play_error()
return { chain = false }
end
else
-- "каждый час напоминай..."
local single, rest2 = body:match("^(%a+)%s*(.*)$")
if single and (single == "час" or single == "минуту" or single == "минута") then
n = 1
unit_norm = (single == "час") and "hours" or "minutes"
sample = (single == "час") and "час" or "минуту"
rest = rest2
else
jarvis.speak("Не понял через сколько повторять.")
jarvis.audio.play_error()
return { chain = false }
end
end
-- Strip optional reminder verb
local text_body = (rest or ""):gsub("^напоминай%s+", "")
:gsub("^напомни%s+", "")
:gsub("^remind me%s+", "")
:gsub("^[%s,:%.]+", "")
:gsub("%s+$", "")
if text_body == "" then text_body = "Напоминание." end
local schedule = string.format("every %d %s", n, unit_norm)
local ok, err = pcall(function()
jarvis.scheduler.add({
name = "Recurring",
schedule = schedule,
action = { type = "speak", text = text_body }
})
end)
if not ok then
jarvis.log("warn", "scheduler.add: " .. tostring(err))
jarvis.speak("Не получилось.")
jarvis.audio.play_error()
return { chain = false }
end
jarvis.speak("Буду напоминать каждые " .. sample .. ".")
jarvis.audio.play_ok()
return { chain = false }