diff --git a/resources/commands/net_info/command.toml b/resources/commands/net_info/command.toml new file mode 100644 index 0000000..4c77f99 --- /dev/null +++ b/resources/commands/net_info/command.toml @@ -0,0 +1,56 @@ +# Network info — WiFi SSID, local IP, public IP via PowerShell + free service. +# Separate from existing `network` pack which likely covers different things. + +[[commands]] +id = "net.wifi_ssid" +type = "lua" +script = "wifi.lua" +sandbox = "full" +timeout = 5000 + +[commands.phrases] +ru = [ + "к какой сети я подключен", + "какой wi-fi", + "какой вайфай", + "какая сеть", + "имя сети", +] +en = ["what wifi", "what network", "wifi name", "ssid"] +ua = ["до якої мережі"] + + +[[commands]] +id = "net.local_ip" +type = "lua" +script = "local_ip.lua" +sandbox = "full" +timeout = 5000 + +[commands.phrases] +ru = [ + "мой локальный ip", + "мой ip", + "айпи адрес", + "локальный адрес", +] +en = ["my ip", "local ip", "ip address"] +ua = ["моя айпі", "локальна адреса"] + + +[[commands]] +id = "net.public_ip" +type = "lua" +script = "public_ip.lua" +sandbox = "full" +timeout = 10000 + +[commands.phrases] +ru = [ + "мой внешний ip", + "публичный ip", + "внешний айпи", + "какой у меня внешний", +] +en = ["public ip", "external ip", "what's my ip online"] +ua = ["зовнішня айпі"] diff --git a/resources/commands/net_info/local_ip.lua b/resources/commands/net_info/local_ip.lua new file mode 100644 index 0000000..55c0b50 --- /dev/null +++ b/resources/commands/net_info/local_ip.lua @@ -0,0 +1,18 @@ +-- Get first non-loopback IPv4 address via PowerShell Get-NetIPAddress. +local ps = "(Get-NetIPAddress -AddressFamily IPv4 | " .. + "Where-Object {$_.IPAddress -notlike '127.*' -and $_.IPAddress -notlike '169.*'} | " .. + "Select-Object -First 1).IPAddress" +local res = jarvis.system.exec(string.format( + 'powershell -NoProfile -Command "%s"', ps +)) + +if not res.success then + return jarvis.cmd.error("Не получилось узнать IP.") +end + +local ip = (res.stdout or ""):gsub("%s+", "") +if ip == "" then + return jarvis.cmd.not_found("Нет активного сетевого подключения.") +end + +return jarvis.cmd.ok("Локальный адрес: " .. ip .. ".") diff --git a/resources/commands/net_info/public_ip.lua b/resources/commands/net_info/public_ip.lua new file mode 100644 index 0000000..74968c2 --- /dev/null +++ b/resources/commands/net_info/public_ip.lua @@ -0,0 +1,12 @@ +-- Free public-IP services: api.ipify.org → plain text body. +local r = jarvis.http.get("https://api.ipify.org") +if not r or not r.ok then + return jarvis.cmd.error("Не получилось узнать внешний IP.") +end + +local ip = (r.body or ""):gsub("%s+", "") +if ip == "" then + return jarvis.cmd.error("Сервис вернул пустой ответ.") +end + +return jarvis.cmd.ok("Внешний адрес: " .. ip .. ".") diff --git a/resources/commands/net_info/wifi.lua b/resources/commands/net_info/wifi.lua new file mode 100644 index 0000000..a708fd1 --- /dev/null +++ b/resources/commands/net_info/wifi.lua @@ -0,0 +1,18 @@ +-- "Какая сеть" → `netsh wlan show interfaces` → grep SSID +local res = jarvis.system.exec('netsh wlan show interfaces') +if not res.success then + return jarvis.cmd.error("Не получилось получить данные о Wi-Fi.") +end + +local out = res.stdout or "" +local ssid = out:match("SSID%s+:%s+(.-)\r?\n") +-- Russian Windows: "SSID : MyNetwork" +-- The non-localised case: same format +if not ssid then ssid = out:match("Имя сети%s+:%s+(.-)\r?\n") end +ssid = ssid and ssid:gsub("^%s+", ""):gsub("%s+$", "") or "" + +if ssid == "" or ssid == "(нет)" or ssid == "(none)" then + return jarvis.cmd.not_found("Wi-Fi не подключен или адаптер выключен.") +end + +return jarvis.cmd.ok("Сеть: " .. ssid .. ".") diff --git a/resources/commands/power/act.lua b/resources/commands/power/act.lua index b189e17..84d6d62 100644 --- a/resources/commands/power/act.lua +++ b/resources/commands/power/act.lua @@ -1,45 +1,60 @@ +-- Power-management dispatcher: shutdown/restart/sleep/hibernate/logoff/cancel/lock. +-- All commands share this script; behaviour selected by jarvis.context.command_id. +-- Modernised to use jarvis.speak (TTS backend abstraction) + jarvis.cmd helpers. + local cmd_id = jarvis.context.command_id local lang = jarvis.context.language local actions = { - shutdown_pc = { cmd = 'shutdown.exe /s /t 30 /c "J.A.R.V.I.S.: shutdown in 30s. Say cancel to abort."', label = lang == "ru" and "Выключение через 30 секунд" or "Shutdown in 30 sec" }, - restart_pc = { cmd = 'shutdown.exe /r /t 30 /c "J.A.R.V.I.S.: restart in 30s. Say cancel to abort."', label = lang == "ru" and "Перезагрузка через 30 секунд" or "Restart in 30 sec" }, - logoff_user = { cmd = 'shutdown.exe /l', label = lang == "ru" and "Выход из системы" or "Sign out" }, - sleep_pc = { cmd = 'rundll32.exe powrprof.dll,SetSuspendState 0,1,0', label = lang == "ru" and "Сон" or "Sleep" }, - hibernate_pc = { cmd = 'shutdown.exe /h', label = lang == "ru" and "Гибернация" or "Hibernate" }, - cancel_shutdown = { cmd = 'shutdown.exe /a', label = lang == "ru" and "Выключение отменено" or "Shutdown cancelled" }, + shutdown_pc = { + cmd = 'shutdown.exe /s /t 30 /c "J.A.R.V.I.S.: shutdown in 30s. Say cancel to abort."', + label = lang == "ru" and "Выключение через 30 секунд" or "Shutdown in 30 sec", + warn = true, + }, + restart_pc = { + cmd = 'shutdown.exe /r /t 30 /c "J.A.R.V.I.S.: restart in 30s. Say cancel to abort."', + label = lang == "ru" and "Перезагрузка через 30 секунд" or "Restart in 30 sec", + warn = true, + }, + logoff_user = { cmd = 'shutdown.exe /l', + label = lang == "ru" and "Выход из системы" or "Sign out" }, + sleep_pc = { cmd = 'rundll32.exe powrprof.dll,SetSuspendState 0,1,0', + label = lang == "ru" and "Сон" or "Sleep" }, + hibernate_pc = { cmd = 'shutdown.exe /h', + label = lang == "ru" and "Гибернация" or "Hibernate" }, + cancel_shutdown = { cmd = 'shutdown.exe /a', + label = lang == "ru" and "Выключение отменено" or "Shutdown cancelled" }, + lock_workstation = { cmd = 'rundll32.exe user32.dll,LockWorkStation', + label = lang == "ru" and "Компьютер заблокирован" or "PC locked" }, } local a = actions[cmd_id] if not a then - jarvis.audio.play_error() - return { chain = false } + return jarvis.cmd.error("Unknown power command.") end local res = jarvis.system.exec(a.cmd) local ok = res.success +-- shutdown /a returns non-zero if there was no pending shutdown — treat as success +-- (idempotent "cancel if any"). if not ok and cmd_id == "cancel_shutdown" then ok = true end -if ok then - jarvis.system.notify("Power", a.label) - jarvis.log("info", "power: " .. cmd_id .. " -> " .. a.label) - local say = a.label .. ". " .. (cmd_id == "shutdown_pc" or cmd_id == "restart_pc" - and (lang == "ru" and "Скажи отмени если передумал, сэр." or "Say cancel to abort, sir.") - or "") - local sapi = say:gsub("'", "''"):gsub("\r?\n", " ") - local ps = string.format( - [[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')]], - sapi - ) - jarvis.system.exec(string.format('powershell -NoProfile -Command "%s"', ps:gsub('"', '\\"'))) - jarvis.audio.play_ok() -else +if not ok then jarvis.log("error", "power " .. cmd_id .. " failed: " .. tostring(res.stderr):sub(1, 200)) - jarvis.system.notify("Power", (lang == "ru" and "Не получилось: " or "Failed: ") .. cmd_id) - jarvis.audio.play_error() + return jarvis.cmd.error((lang == "ru" and "Не получилось: " or "Failed: ") .. cmd_id) end -return { chain = false } +jarvis.system.notify("Power", a.label) +jarvis.log("info", "power: " .. cmd_id .. " -> " .. a.label) + +-- For destructive ops with a 30-second window, give the user the abort hint. +local say = a.label +if a.warn then + say = say .. ". " .. (lang == "ru" and "Скажите отмени если передумали." + or "Say cancel to abort.") +end + +return jarvis.cmd.ok(say) diff --git a/resources/commands/screenshot/command.toml b/resources/commands/screenshot/command.toml new file mode 100644 index 0000000..fc7df0c --- /dev/null +++ b/resources/commands/screenshot/command.toml @@ -0,0 +1,38 @@ +# Quick screenshot pack — capture screen, save somewhere useful. +# Unlike vision/, no LLM call — just the capture. Faster + works offline. + +[[commands]] +id = "screenshot.to_clipboard" +type = "lua" +script = "to_clipboard.lua" +sandbox = "full" +timeout = 5000 + +[commands.phrases] +ru = [ + "сделай скриншот", + "сделай скрин", + "скопируй экран", + "скриншот в буфер", + "захвати экран", +] +en = ["take a screenshot", "screenshot", "capture screen"] +ua = ["зроби скрін", "захопи екран"] + + +[[commands]] +id = "screenshot.to_file" +type = "lua" +script = "to_file.lua" +sandbox = "full" +timeout = 5000 + +[commands.phrases] +ru = [ + "сохрани скриншот", + "сохрани скрин", + "скриншот в файл", + "сохрани экран", +] +en = ["save screenshot", "screenshot to file"] +ua = ["збережи скрін"] diff --git a/resources/commands/screenshot/to_clipboard.lua b/resources/commands/screenshot/to_clipboard.lua new file mode 100644 index 0000000..56d07d7 --- /dev/null +++ b/resources/commands/screenshot/to_clipboard.lua @@ -0,0 +1,25 @@ +-- Capture full screen → put PNG into clipboard via PowerShell. +-- One-liner with System.Drawing + System.Windows.Forms.Clipboard.SetImage. + +local ps = [[ +Add-Type -AssemblyName System.Windows.Forms +Add-Type -AssemblyName System.Drawing +$bounds = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds +$bmp = New-Object System.Drawing.Bitmap $bounds.Width, $bounds.Height +$g = [System.Drawing.Graphics]::FromImage($bmp) +$g.CopyFromScreen($bounds.Location, [System.Drawing.Point]::Empty, $bounds.Size) +[System.Windows.Forms.Clipboard]::SetImage($bmp) +$g.Dispose(); $bmp.Dispose() +]] + +local res = jarvis.system.exec(string.format( + 'powershell -NoProfile -ExecutionPolicy Bypass -Command "%s"', + ps:gsub('"', '\\"'):gsub("\r?\n", "; ") +)) + +if not res.success then + jarvis.log("warn", "screenshot to clipboard failed: " .. tostring(res.stderr):sub(1, 200)) + return jarvis.cmd.error("Не получилось сделать скриншот.") +end + +return jarvis.cmd.ok("Скриншот в буфере.") diff --git a/resources/commands/screenshot/to_file.lua b/resources/commands/screenshot/to_file.lua new file mode 100644 index 0000000..e0266da --- /dev/null +++ b/resources/commands/screenshot/to_file.lua @@ -0,0 +1,36 @@ +-- Capture full screen → save PNG under ~/Pictures/Screenshots/jarvis-YYYYMMDD-HHMMSS.png +local t = jarvis.context.time +local stamp = string.format("%04d%02d%02d-%02d%02d%02d", + t.year, t.month, t.day, t.hour, t.minute, t.second or 0) + +local userprofile = jarvis.system.env("USERPROFILE") or "C:\\Users\\Public" +local dir = userprofile .. "\\Pictures\\Screenshots" +local path = string.format("%s\\jarvis-%s.png", dir, stamp) + +-- Ensure dir exists +jarvis.fs.mkdir(dir) + +local escaped = path:gsub("'", "''") +local ps = string.format([[ +Add-Type -AssemblyName System.Windows.Forms +Add-Type -AssemblyName System.Drawing +$bounds = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds +$bmp = New-Object System.Drawing.Bitmap $bounds.Width, $bounds.Height +$g = [System.Drawing.Graphics]::FromImage($bmp) +$g.CopyFromScreen($bounds.Location, [System.Drawing.Point]::Empty, $bounds.Size) +$bmp.Save('%s', [System.Drawing.Imaging.ImageFormat]::Png) +$g.Dispose(); $bmp.Dispose() +]], escaped) + +local res = jarvis.system.exec(string.format( + 'powershell -NoProfile -ExecutionPolicy Bypass -Command "%s"', + ps:gsub('"', '\\"'):gsub("\r?\n", "; ") +)) + +if not res.success then + jarvis.log("warn", "screenshot to file failed: " .. tostring(res.stderr):sub(1, 200)) + return jarvis.cmd.error("Не получилось сохранить скриншот.") +end + +jarvis.system.notify("Скриншот", path) +return jarvis.cmd.ok("Скриншот сохранён в папку Скриншоты.")