feat: Wave 1 — 10 new packs (75 → 85 rust packs)

User picked from the fishki roadmap; this commit ships the 10 easy/medium
ones in one batch. No Rust core changes — all pure Lua + jarvis.* APIs.

Pack catalog

magic_8ball
  - "ответь да или нет" / "магический шар" / "что скажешь"
  - 15 канонических ответов, random pick. Pure fun.

github_issues (2 cmds)
  - "какие issues" / "открытые issues" — gh issue list по сохранённому репо
  - "мои issues" / "что мне назначено" — gh issue list --assignee @me

weather_extended (2 cmds, Open-Meteo, no key)
  - "погода завтра" — temp min/max + conditions + rain note
  - "прогноз на неделю" — общий диапазон температур
  - Auto-detects location via ipinfo.io if not in memory; stores lat/lon/city.

rss_reader (3 cmds)
  - "добавь rss <url>" — extracts URL, persists via memory под ключом "rss.<domain>"
  - "что в ленте" — fetches first feed, parses <title> tags, speaks top 3
  - "какие у меня rss" — lists subscribed feed domains

ics_event
  - "добавь встречу завтра в 15:00 обсудить проект"
  - Parses time (HH:MM или "в HH") + date keywords (сегодня/завтра/послезавтра)
  - Writes valid iCalendar v2.0 file to ~/Documents/jarvis-events/
  - jarvis.system.open → дефолтный handler (Outlook/Mail/whatever)

backup
  - "сделай бекап" — PowerShell Compress-Archive всех state files из APPDATA/Jarvis
  - Outputs to ~/Documents/jarvis-backup-<YYYYMMDD-HHMMSS>.zip
  - Bundles: long_term_memory, schedule, macros, profiles/, active_profile, llm_backend, settings

clip_history (3 cmds, 20-slot rolling buffer)
  - "запиши буфер" — push current clipboard onto memory keys clip.0..clip.19
  - "что я копировал" — speak preview of last 3
  - "верни первый/второй/третий буфер" — restore clip.N to clipboard

notif_queue (2 cmds)
  - "что я пропустил" — enumerate memory keys "notif.*", speak last 5 by recency
  - "очисти уведомления" — purge all notif.* keys
  - Producers (scheduler/macros/llm) can later push to memory[notif.<ts>] = text

password_vault (3 cmds, Windows DPAPI)
  - "сохрани пароль от GitHub" — encrypts current clipboard content via DPAPI
    (CurrentUser scope), base64-stored in memory[vault.GitHub]. Clears clipboard.
    Password is NEVER spoken or written to disk in plaintext.
  - "пароль от GitHub" — decrypts via DPAPI, restores to clipboard for 30 sec,
    schedules auto-clear via jarvis.scheduler. Speaks only "Пароль от X в буфере."
  - "какие у меня пароли" — list of stored service names.

habit_streaks (2 cmds, integrates with habit_nudge)
  - "сколько дней подряд" / "статистика привычек" — reads memory keys
    "habit_streak.<habit>.<YYYY-MM-DD>", computes consecutive-day streak per habit.
  - "я попил воды" / "отметь привычку" — marks today's check-in.
    Maps voice to habit: воды→water, размял/зарядк→stretch, глаз→eyes.

Tests: 6 commands tests pass (auto-validate the 10 new packs).
This commit is contained in:
Bossiara13 2026-05-16 01:04:23 +03:00
parent 4d3d664abd
commit c4b22618f8
30 changed files with 1086 additions and 0 deletions

View file

@ -0,0 +1,34 @@
-- "Добавь rss https://example.com/feed" or with named alias
local phrase = (jarvis.context.phrase or "")
local body = jarvis.text.strip_trigger(phrase:lower(), {
"добавь rss",
"подпишись на",
"запомни ленту",
"добавь ленту",
"add rss",
"subscribe to",
})
body = body:gsub("^[%s,:%.]+", ""):gsub("%s+$", "")
-- Preserve original casing (URLs are case-sensitive after host).
local raw = phrase
local lower_idx = raw:lower():find(body, 1, true)
if lower_idx then
body = raw:sub(lower_idx, lower_idx + #body - 1)
end
if body == "" then
return jarvis.cmd.error("Какую ленту добавить?")
end
local url = body:match("(https?://%S+)")
if not url then
return jarvis.cmd.error("Не нашёл URL.")
end
-- Use the URL itself as the key (or extract domain as nicer alias).
local domain = url:match("https?://([^/]+)") or "rss"
local key = "rss." .. domain
jarvis.memory.remember(key, url)
return jarvis.cmd.ok("Лента " .. domain .. " добавлена.")

View file

@ -0,0 +1,54 @@
# RSS reader — fetch top headlines from a stored feed.
[[commands]]
id = "rss.add"
type = "lua"
script = "add.lua"
sandbox = "standard"
timeout = 3000
[commands.phrases]
ru = [
"добавь rss",
"подпишись на",
"запомни ленту",
"добавь ленту",
]
en = ["add rss", "subscribe to"]
ua = ["додай rss"]
[[commands]]
id = "rss.read"
type = "lua"
script = "read.lua"
sandbox = "full"
timeout = 15000
[commands.phrases]
ru = [
"что в ленте",
"новости из ленты",
"почитай rss",
"новости из rss",
"новые статьи",
]
en = ["what's in feed", "read rss", "latest articles"]
ua = ["що в стрічці"]
[[commands]]
id = "rss.list"
type = "lua"
script = "list.lua"
sandbox = "minimal"
timeout = 3000
[commands.phrases]
ru = [
"какие у меня rss",
"список лент",
"мои подписки",
]
en = ["list feeds", "my feeds"]
ua = ["мої стрічки"]

View file

@ -0,0 +1,13 @@
local all = jarvis.memory.all()
local feeds = {}
for _, rec in ipairs(all) do
if rec.key:sub(1, 4) == "rss." then
table.insert(feeds, rec.key:sub(5))
end
end
if #feeds == 0 then
return jarvis.cmd.ok("Лент пока нет.")
end
return jarvis.cmd.ok("Подписан на: " .. table.concat(feeds, ", ") .. ".")

View file

@ -0,0 +1,41 @@
-- Fetch one stored feed and speak top 3 titles.
-- If multiple feeds, picks the first one.
local all = jarvis.memory.all()
local feed_url = nil
local feed_name = nil
for _, rec in ipairs(all) do
if rec.key:sub(1, 4) == "rss." then
feed_url = rec.value
feed_name = rec.key:sub(5)
break
end
end
if not feed_url then
return jarvis.cmd.not_found("Лент не добавлено. Скажите 'добавь rss и URL'.")
end
local r = jarvis.http.get(feed_url)
if not r or not r.ok then
return jarvis.cmd.error("Не получилось загрузить ленту.")
end
local body = r.body or ""
-- Crude regex parser. Works for most RSS 2.0 + Atom feeds.
local titles = {}
for t in body:gmatch("<title[^>]*>([^<]+)</title>") do
-- Skip the channel/feed title (always first), keep item titles
table.insert(titles, t)
end
if #titles < 2 then
return jarvis.cmd.error("В ленте нет заголовков.")
end
-- titles[1] is channel title; items start at [2]
local sample = math.min(#titles - 1, 3)
local line = "Из " .. feed_name .. ". "
for i = 2, 1 + sample do
line = line .. titles[i] .. ". "
end
return jarvis.cmd.ok(line)