J.A.R.V.I.S-rust/resources/commands/games/launch.lua

111 lines
4.3 KiB
Lua
Raw Permalink Normal View History

arch + features: Lua jarvis.speak() / jarvis.llm() API, /commands GUI page, games / mouse / random_choice packs Architecture — DRY at the Lua API surface: lua/api/tts.rs (new): jarvis.speak(text, opts?). opts.lang = ISO two-letter code (default "ru"); auto-picks first installed voice matching the culture. opts.async = true|false (default true). The 9 packs that previously inlined a 60-character PS-SAPI string for every spoken reply can now reduce to one line. Refactored fun/, dice/, clipboard_read/ as proof — fun/ask.lua went from 75 lines of HTTP+SAPI boilerplate to ~28 lines of intent code. lua/api/llm.rs (new): jarvis.llm(messages, opts?). messages is a list of {role, content} tables, opts.max_tokens / .temperature / .top_p. Returns the assistant string or nil. Gated by sandbox allows_http() (so Standard+ packs only). Internally uses the existing jarvis_core::llm::LlmClient::complete_with, no duplicated HTTP plumbing. llm/client.rs: split complete() into complete_with(messages, max_tokens, temperature, top_p) for the new caller; old complete() is now a thin wrapper at temp=0.7 top_p=1.0 for backward compat. GUI — /commands page rewrite: Replaces the "Раздел в разработке" placeholder. Calls Tauri command get_commands_list (already existed in tauri_commands/commands.rs, was wired but unused). Renders a search-filterable card grid: - cmd id (monospace) - type badge with colour by kind (lua=blue, ahk=red, cli=grey, voice=green, terminate=red, stop_chaining=violet) - sandbox badge - description line (if non-empty) - all phrases for currentLanguage, fallback to en, fallback to first available; each phrase in a small inline pill Toolbar: text input with magnifier icon + reload button (↻). Counter line shows visible/total + "Скажи Джарвис + любую фразу" hint. Counts down as you type the filter. New packs (3, total 33 → 36): games/ — launch_game / list_games. Reads or creates %USERPROFILE%\Documents\jarvis-games.json (the sample preloads dota, cs2, elden ring, witcher 3, factorio + minecraft launcher path + fortnite epic URI). Trigger-strip + fuzzy name/alias match, then launches via steam://rungameid/N (Steam), epic:// (Epic Games), or start "" "path" (anything else). list_games speaks the configured library. mouse/ — left/right/middle/double click + scroll up/down. Single dispatch.lua + _mouse_helper.ps1. PS P/Invokes user32!mouse_event with the right flag pair (LEFTDOWN+LEFTUP / etc.), 30ms gap, doubles do two LEFT cycles with a 50ms gap, wheel uses ±360 ticks. random_choice/ — "выбери из X или Y или Z". Strips the trigger, splits on " или " / " or " / ", " / " либо " / " чи ", math.random picks one, speaks "Я выбираю N". cargo test commands::tests still 3/3. 36 packs verified via jarvis-gui startup log.
2026-05-15 12:58:16 +03:00
local lang = jarvis.context.language
local userprofile = jarvis.system.env("USERPROFILE") or "C:\\Users\\Public"
local config_path = userprofile .. "\\Documents\\jarvis-games.json"
if not jarvis.fs.exists(config_path) then
local sample = [[[
{"name": "dota", "aliases": ["доту", "доту 2", "dota 2"], "steam_appid": 570},
{"name": "cs2", "aliases": ["кс", "контру", "контр страйк"], "steam_appid": 730},
{"name": "elden ring", "aliases": ["элден", "элден ринг"], "steam_appid": 1245620},
{"name": "witcher 3", "aliases": ["ведьмак", "ведьмака 3"], "steam_appid": 292030},
{"name": "factorio", "aliases": ["факторио"], "steam_appid": 427520},
{"name": "minecraft", "aliases": ["майнкрафт", "майн"], "path": "C:\\Program Files (x86)\\Minecraft Launcher\\MinecraftLauncher.exe"},
{"name": "fortnite", "aliases": ["фортнайт"], "epic_uri": "com.epicgames.launcher://apps/Fortnite?action=launch&silent=true"}
]
]]
jarvis.fs.write(config_path, sample)
jarvis.system.notify(
lang == "ru" and "Игры" or "Games",
lang == "ru" and "Создан шаблон ~/Documents/jarvis-games.json — добавь свои игры и повтори"
or "Template at Documents/jarvis-games.json — edit and retry"
)
jarvis.system.open(config_path)
jarvis.audio.play_not_found()
return { chain = false }
end
local content = jarvis.fs.read(config_path)
local entries = {}
local cursor = 1
while true do
local s, e, block = content:find("({[^{}]+})", cursor)
if not s then break end
cursor = e + 1
local name = block:match('"name"%s*:%s*"([^"]+)"')
if name then
local entry = { name = name, name_lower = name:lower(), aliases = {} }
for alias in block:gmatch('"([^"]+)"') do
entry.aliases[#entry.aliases + 1] = alias:lower()
end
entry.steam_appid = tonumber(block:match('"steam_appid"%s*:%s*(%d+)'))
entry.path = block:match('"path"%s*:%s*"([^"]+)"')
entry.epic_uri = block:match('"epic_uri"%s*:%s*"([^"]+)"')
table.insert(entries, entry)
end
end
if #entries == 0 then
jarvis.system.notify("Games", lang == "ru" and "Пусто в конфиге" or "Empty config")
jarvis.audio.play_not_found()
return { chain = false }
end
local phrase = (jarvis.context.phrase or ""):lower()
local triggers = {
"запусти игру", "включи игру", "хочу поиграть", "поиграем", "запусти", "включи",
"launch game", "play game", "launch",
"запусти гру", "увімкни гру",
}
local query = phrase
for _, t in ipairs(triggers) do
local s, e = string.find(query, t, 1, true)
if s == 1 then
query = query:sub(e + 1)
break
end
end
query = query:gsub("^%s+", ""):gsub("%s+$", "")
local function name_matches(e, q)
if e.name_lower == q then return true end
if string.find(e.name_lower, q, 1, true) then return true end
for _, a in ipairs(e.aliases) do
if a == q or string.find(q, a, 1, true) then return true end
end
return false
end
local match
for _, e in ipairs(entries) do
if name_matches(e, query) then match = e; break end
end
if not match then
jarvis.system.notify("Games", (lang == "ru" and "Не нашёл: " or "Not found: ") .. query)
jarvis.audio.play_not_found()
return { chain = false }
end
local launched = false
if match.steam_appid then
jarvis.system.open("steam://rungameid/" .. tostring(match.steam_appid))
launched = true
elseif match.epic_uri then
jarvis.system.open(match.epic_uri)
launched = true
elseif match.path and jarvis.fs.exists(match.path) then
jarvis.system.exec(string.format('start "" "%s"', match.path))
launched = true
end
if launched then
jarvis.system.notify(lang == "ru" and "Запускаю" or "Launching", match.name)
jarvis.speak((lang == "ru" and "Запускаю " or "Launching ") .. match.name .. ".", { lang = lang })
jarvis.audio.play_ok()
else
jarvis.system.notify("Games", (lang == "ru" and "Не настроен запуск: " or "No launch info: ") .. match.name)
jarvis.audio.play_error()
end
return { chain = false }