J.A.R.V.I.S-rust/resources/commands/reminders/set.lua

120 lines
4.5 KiB
Lua
Raw Normal View History

feat: 5 new packs + dotenvy + regen icon.ico + shortcuts redo Iron Man icon regenerated from resources/icons/icon.png at sizes 16/24/32/48/64/128/256 via Pillow (make_ico.py committed alongside); icon.ico is now a clean 83 KB multi-res for sharp display at any scale instead of whatever placeholder Tauri originally emitted. dotenvy added to workspace deps. jarvis-app/main.rs and jarvis-gui/ main.rs both walk up from current_exe() looking for dev.env (up to 5 parent levels), so the Desktop shortcut can launch the exe directly without a wrapper .bat to pre-set GROQ_TOKEN. Falls back to dotenvy::dotenv() (cwd lookup) if nothing found near the binary. 5 new portable Lua command packs. Bumps total from 21 to 26. cargo test -p jarvis-core --lib commands::tests still passes 3/3. reminders/ — set_reminder. Parses "напомни через N (секунд|минут| часов) <body>" or English equivalents; understands one Russian word numerals (один, две, пять, десять, пятнадцать, ...) for the count when speech recognition returns words instead of digits. Defaults to 5 minutes if number/unit missing. Spawns a detached PowerShell that Start-Sleeps then fires: BurntToast if installed, System.Speech SAPI (ru-RU voice preference), and a WScript.Shell.Popup as the guaranteed-visible last resort. date_query/ — today / tomorrow / yesterday. Single answer.lua keyed by command_id, computes the offset via PowerShell Get-Date.AddDays() in ru-RU culture so the weekday/month come out in Russian, then speaks via SAPI. voice_type/ — type_text. Strips the trigger, drops the remainder to clipboard via jarvis.system.clipboard.set, then synthesises Ctrl+V through user32!keybd_event so it lands in whatever window currently has focus. Works in any text field (note app, browser, IDE, Telegram, etc.). dice/ — coin_flip / roll_dice / random_number. Single roll.lua, seeds math.random with os.time + jitter, speaks the result. "1-6" for dice, "1-100" for random. stopwatch/ — start / check / stop. Uses jarvis.state.get/set (persisted via jarvis-core's settings DB) to remember the start timestamp, computes elapsed via jarvis.context.time.timestamp, formats as "X сек" / "X мин Y сек" / "X ч Y мин Z сек". All new packs follow the established patterns (sandbox=full, PS-via- exec helper where needed, USERPROFILE/SystemDrive everywhere, no hardcoded paths).
2026-05-15 12:28:44 +03:00
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, ["час"] = 60, -- "через час" without number
}
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
local title_esc = (lang == "ru" and "Напоминание J.A.R.V.I.S." or "J.A.R.V.I.S. reminder"):gsub("'", "''")
local body_esc = body:gsub("'", "''")
local sapi_text = ((lang == "ru" and "Сэр, напоминаю: " or "Reminder: ") .. body):gsub("'", "''"):gsub("\r?\n", " ")
local ps = string.format(
[[Start-Sleep -Seconds %d; ]]
.. [[$null = New-Object -ComObject Shell.Application; ]]
.. [[try { ]]
.. [[ Import-Module BurntToast -ErrorAction SilentlyContinue; ]]
.. [[ New-BurntToastNotification -Text '%s', '%s' -ErrorAction SilentlyContinue ]]
.. [[} catch {}; ]]
.. [[Add-Type -AssemblyName System.Speech; ]]
.. [[$s = New-Object System.Speech.Synthesis.SpeechSynthesizer; ]]
.. [[foreach ($v in $s.GetInstalledVoices()) { if ($v.VoiceInfo.Culture.TwoLetterISOLanguageName -eq 'ru') { try { $s.SelectVoice($v.VoiceInfo.Name); break } catch {} } }; ]]
.. [[$s.Speak('%s'); ]]
.. [[$wsh = New-Object -ComObject WScript.Shell; $wsh.Popup('%s', 30, '%s', 64) | Out-Null]],
total_seconds, title_esc, body_esc, sapi_text, body_esc, title_esc
)
local cmd = string.format(
'powershell -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -Command "%s"',
ps:gsub('"', '\\"')
)
jarvis.system.exec("start /b " .. cmd)
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: %s", total_seconds, body))
jarvis.audio.play_ok()
return { chain = false }