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:
parent
4d3d664abd
commit
c4b22618f8
30 changed files with 1086 additions and 0 deletions
34
resources/commands/rss_reader/add.lua
Normal file
34
resources/commands/rss_reader/add.lua
Normal 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 .. " добавлена.")
|
||||
54
resources/commands/rss_reader/command.toml
Normal file
54
resources/commands/rss_reader/command.toml
Normal 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 = ["мої стрічки"]
|
||||
13
resources/commands/rss_reader/list.lua
Normal file
13
resources/commands/rss_reader/list.lua
Normal 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, ", ") .. ".")
|
||||
41
resources/commands/rss_reader/read.lua
Normal file
41
resources/commands/rss_reader/read.lua
Normal 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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue