-- Reminder — schedule a one-shot Speak action via the persistent scheduler. -- -- Why this rewrite: the previous version spawned `Start-Sleep` PowerShell -- subprocesses that didn't survive jarvis-app restart. Now we use -- `jarvis.scheduler.add(...)` which persists to schedule.json and fires -- correctly even if the daemon is restarted in between. local lang = jarvis.context.language local phrase = (jarvis.context.phrase or ""):lower() local triggers = { "поставь напоминание через", "установи таймер на", "напомни мне через", "напомни через", "напомни", "remind me in", "set reminder in", "set timer for", "timer for", "таймер на", } local rest = phrase for _, t in ipairs(triggers) do local s, e = string.find(rest, t, 1, true) if s == 1 then rest = rest:sub(e + 1) break end end rest = rest:gsub("^%s+", "") local unit_words = { ["секунд"] = 1, ["секунду"] = 1, ["секунды"] = 1, ["second"] = 1, ["seconds"] = 1, ["sec"] = 1, ["минут"] = 60, ["минуту"] = 60, ["минуты"] = 60, ["мин"] = 60, ["minute"] = 60, ["minutes"] = 60, ["min"] = 60, ["час"] = 3600, ["часа"] = 3600, ["часов"] = 3600, ["часик"] = 3600, ["hour"] = 3600, ["hours"] = 3600, ["hr"] = 3600, } local rus_numerals = { ["один"] = 1, ["одну"] = 1, ["одна"] = 1, ["полторы"] = 1, ["полтора"] = 1, ["два"] = 2, ["две"] = 2, ["три"] = 3, ["четыре"] = 4, ["пять"] = 5, ["шесть"] = 6, ["семь"] = 7, ["восемь"] = 8, ["девять"] = 9, ["десять"] = 10, ["пятнадцать"] = 15, ["двадцать"] = 20, ["тридцать"] = 30, ["сорок"] = 40, ["пятьдесят"] = 50, } local number, unit, body = nil, nil, nil local num_str, rest_after_num = rest:match("^(%d+)%s+(.+)") if num_str then number = tonumber(num_str) rest = rest_after_num else local first_word, after = rest:match("^(%S+)%s*(.*)") if first_word and rus_numerals[first_word] then number = rus_numerals[first_word] rest = after or "" end end if rest and #rest > 0 then local first_word, after = rest:match("^(%S+)%s*(.*)") if first_word then for w, secs in pairs(unit_words) do if first_word:sub(1, #w) == w then unit = secs body = after or "" break end end end end if not number then number = 5 if not unit then unit = 60 end body = rest elseif not unit then unit = 60 body = rest end local total_seconds = number * unit if total_seconds < 1 then total_seconds = 1 end if total_seconds > 86400 then total_seconds = 86400 end body = (body or ""):gsub("^%s+", ""):gsub("%s+$", "") if body == "" then body = lang == "ru" and "Напоминание!" or "Reminder!" end local function human_time(sec) if sec < 60 then return sec .. " сек" end if sec < 3600 then return math.floor(sec/60) .. " мин" end return string.format("%d ч %d мин", math.floor(sec/3600), math.floor((sec%3600)/60)) end -- Build the scheduler entry. Scheduler's "in N minutes" wants integer minutes -- so we round up for sub-minute timers; users asking for "10 seconds" will get -- ~1 minute. Honest trade-off vs the more complex previous detached-PS approach. local minutes = math.max(1, math.floor((total_seconds + 59) / 60)) local schedule_str = "in " .. tostring(minutes) .. " minutes" local speak_text = (lang == "ru" and "Сэр, напоминаю: " or "Reminder: ") .. body local ok, err = pcall(function() jarvis.scheduler.add({ name = lang == "ru" and ("Таймер: " .. body) or ("Timer: " .. body), schedule = schedule_str, action = { type = "speak", text = speak_text }, }) end) if not ok then jarvis.log("error", "reminder: scheduler.add failed: " .. tostring(err)) return jarvis.cmd.error(lang == "ru" and "Не получилось поставить напоминание." or "Couldn't set reminder.") end jarvis.system.notify( lang == "ru" and "Таймер установлен" or "Timer set", string.format(lang == "ru" and "Через %s: %s" or "In %s: %s", human_time(total_seconds), body) ) jarvis.log("info", string.format("reminder in %ds (rounded to %d min): %s", total_seconds, minutes, body)) return jarvis.cmd.ok(string.format( lang == "ru" and "Хорошо, напомню через %s." or "Will remind you in %s.", human_time(total_seconds) ))