J.A.R.V.I.S-rust/resources/commands/games/launch.lua
Bossiara13 a16d2401e7 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

110 lines
4.3 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.

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 }