docs+feat: USAGE.md + interesting_fact + mailto packs
USAGE.md (new, ~250 lines)
- Complete voice command reference organised by 13 categories
- Table-formatted for fast skim ("Команда → Что делает")
- Sections: Управление ассистентом / LLM / Память+профили+macros /
Расписание / Окна+апп / Аудио+медиа / Система / Файлы+буфер /
Информация из интернета / Утилиты-калькуляторы / Разработка /
Развлечения / Скриншоты
- Bottom: "Как добавить свою команду" with Lua snippet template,
"Голосовое vs GUI" mapping, troubleshooting checklist (mic / TTS /
LLM / command-not-found / logs).
- Covers all 67 packs visible in resources/commands/.
interesting_fact pack (resources/commands/interesting_fact/, 2 cmds)
- fact.about "удиви меня" / "интересный факт про космос"
→ LLM with "curious conversationalist" prompt,
generic or topic-targeted (1-2 sentences, no fluff).
- fact.history_today "что было в этот день" / "этот день в истории"
→ Wikipedia "On this day" API (RU first, EN fallback)
→ LLM picks top 2-3 events, narrates concisely.
mailto pack (resources/commands/mailto/, 1 cmd)
- mailto.compose "напиши письмо" / "напиши письмо маме"
→ opens default mail client via mailto: URI.
Looks up recipient via jarvis.memory ("email_маме")
if a name is given.
- Pairs well with: jarvis.memory.remember("email_маме", "mom@example.com")
Pack count: 67 → 70. Tests: 112/112.
This commit is contained in:
parent
d3180b7d78
commit
4f83062815
6 changed files with 430 additions and 0 deletions
259
USAGE.md
Normal file
259
USAGE.md
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
# Voice command reference
|
||||
|
||||
Все голосовые команды J.A.R.V.I.S. (текущее состояние, **67 паков**).
|
||||
Каждая команда — это фраза, которую можно произнести после wake-word ("Джарвис").
|
||||
|
||||
Группы:
|
||||
|
||||
- [Управление ассистентом](#управление-ассистентом)
|
||||
- [LLM / мозг](#llm--мозг)
|
||||
- [Память / профили / macros](#память--профили--macros)
|
||||
- [Расписание / напоминания / привычки](#расписание--напоминания--привычки)
|
||||
- [Окна / приложения / процессы](#окна--приложения--процессы)
|
||||
- [Аудио / медиа](#аудио--медиа)
|
||||
- [Система / устройство](#система--устройство)
|
||||
- [Файлы / буфер / заметки](#файлы--буфер--заметки)
|
||||
- [Информация из интернета](#информация-из-интернета)
|
||||
- [Утилиты-калькуляторы](#утилиты-калькуляторы)
|
||||
- [Разработка](#разработка)
|
||||
- [Развлечения](#развлечения)
|
||||
- [Скриншоты / экран](#скриншоты--экран)
|
||||
|
||||
После каждой команды есть **30-секундное grace-окно** для follow-up без повторного "Джарвис".
|
||||
Конфигурируется через `CONVERSATION_GRACE_MS`.
|
||||
|
||||
---
|
||||
|
||||
## Управление ассистентом
|
||||
|
||||
| Команда (RU) | Что делает |
|
||||
|---|---|
|
||||
| "Помощь" / "что ты умеешь" | Перечисляет категории возможностей |
|
||||
| "Расскажи о себе" / "кто ты такой" | Краткое представление (LLM варьирует) |
|
||||
| "Сколько команд знаешь" | Health snapshot — все активные бэкенды + счётчики |
|
||||
| "Диагностика" / "доложи о себе" | Одной строкой: TTS, LLM, профиль, памяти/задач |
|
||||
| "Повтори за мной X" | Эхо — для теста микрофона + TTS |
|
||||
| "Что я сказал" | Что услышал из последней фразы |
|
||||
| "Стоп" / "хватит" / "прекрати" | Прерывает текущую команду |
|
||||
| "Сбрось контекст" / "забудь разговор" | Очищает историю LLM |
|
||||
| "Повтори последнее" / "повтори ответ" | Озвучивает последний ответ LLM |
|
||||
|
||||
## LLM / мозг
|
||||
|
||||
| Команда | Что делает |
|
||||
|---|---|
|
||||
| "Переключись на локальный" / "перейди на оллама" | Hot-swap на Ollama (persistent в БД) |
|
||||
| "Переключись на облако" / "используй грок" | Hot-swap на Groq |
|
||||
| "Какой у тебя мозг" / "облако или локально" | Текущий backend |
|
||||
|
||||
Любая фраза без распознанной команды → LLM router (если включён) → если нет матча → LLM chat fallback.
|
||||
|
||||
## Память / профили / macros
|
||||
|
||||
| Команда | Что делает |
|
||||
|---|---|
|
||||
| "Запомни что я люблю чай улун" | Сохраняет факт |
|
||||
| "Что ты помнишь про чай" | Substring-поиск по памяти |
|
||||
| "Что ты знаешь обо мне" | Все факты |
|
||||
| "Забудь про X" | Удалить факт |
|
||||
| "Режим работа" / "режим игра" / "режим сон" / "режим за рулём" / "обычный режим" | Профиль (5 встроенных) |
|
||||
| "Какой сейчас режим" | Активный профиль |
|
||||
| "Запиши макрос имя" | Начать запись макроса |
|
||||
| "Сохрани макрос" | Завершить запись и сохранить |
|
||||
| "Отмени макрос" | Отменить запись |
|
||||
| "Запусти макрос имя" | Воспроизвести (с интервалом 800мс) |
|
||||
| "Какие у меня макросы" / "удали макрос имя" | Управление списком |
|
||||
|
||||
Также: GUI страницы `/memory` (добавить/забыть факты), `/macros` (запуск/удаление), `/scheduler` (отмена задач).
|
||||
|
||||
## Расписание / напоминания / привычки
|
||||
|
||||
| Команда | Что делает |
|
||||
|---|---|
|
||||
| "Напомни через 5 минут выключить кофеварку" | One-shot reminder |
|
||||
| "Напомни в 18:00 забрать ребёнка" | Today at HH:MM |
|
||||
| "Каждые 2 часа напоминай попить воды" | Interval |
|
||||
| "Каждый день в 9:00 делай брифинг" | Daily |
|
||||
| "Что у меня запланировано" / "очисти расписание" | Управление |
|
||||
| "Отмени напоминание про воду" | Substring-match remove |
|
||||
| "Запусти помодоро" / "стоп помодоро" | 25/5 циклы |
|
||||
| "Напоминай мне пить воду" | Каждые 2 часа |
|
||||
| "Напоминай размяться" | Каждые 50 минут |
|
||||
| "Напоминай отдыхать глазам" | 20-20-20 (каждые 20 мин) |
|
||||
| "Отключи все привычки" | Стоп всех habit_nudge задач |
|
||||
| "Настрой утренний брифинг на 9:00" | Daily briefing в указанное время |
|
||||
| "Утренний брифинг" | Запустить брифинг сейчас |
|
||||
| "Отключи утренний брифинг" | Снять daily задачу |
|
||||
|
||||
## Окна / приложения / процессы
|
||||
|
||||
| Команда | Что делает |
|
||||
|---|---|
|
||||
| "Открой Telegram" / "запусти Steam" / "Spotify" | Через `apps/` пак (имя в config) |
|
||||
| "Закрой Telegram" / "убей процесс X" | Process kill |
|
||||
| "Сверни всё" / "разверни окно" / "переключи окно" | Window manager |
|
||||
| "Какие открыты окна" | Список Z-order |
|
||||
| "Сверни активное" / "максимизируй" | Action на foreground |
|
||||
| "Открой папку загрузок" / "файлы" / "проводник" | Explorer |
|
||||
|
||||
## Аудио / медиа
|
||||
|
||||
| Команда | Что делает |
|
||||
|---|---|
|
||||
| "Громче" / "тише" / "громкость на 30" | Volume up/down/set |
|
||||
| "Заглуши" / "сними заглушку" | Mute toggle |
|
||||
| "Какая громкость" | Текущий уровень |
|
||||
| "Открой панель звука" | Sound panel |
|
||||
| "Включи Realtek" / "переключи звук на Headphones" | Output device swap |
|
||||
| "Пауза" / "плей" / "следующий трек" / "предыдущий трек" / "стоп музыка" | Media keys — Spotify / YouTube / Foobar / Yandex Music |
|
||||
| "Запиши голосовую заметку" | (см. notes/) |
|
||||
| "Выключи музыку через 30 минут" | Sleep timer для медиа |
|
||||
|
||||
## Система / устройство
|
||||
|
||||
| Команда | Что делает |
|
||||
|---|---|
|
||||
| "Сколько процессор грузится" / "сколько RAM" / "температура" | Sysinfo |
|
||||
| "Сколько заряда" / "заряд батареи" | Battery % |
|
||||
| "Сколько свободно на диске" / "сколько на диске C" | PowerShell Get-PSDrive |
|
||||
| "Какие у меня диски" | C: D: E: со свободным местом |
|
||||
| "Какая сеть" / "какой WiFi" | Net SSID via netsh |
|
||||
| "Мой IP" / "локальный IP" | Get-NetIPAddress |
|
||||
| "Внешний IP" | api.ipify.org |
|
||||
| "Прибавь яркость" / "тусклее" / "яркость 70" | Brightness via WMI |
|
||||
| "Тёмная тема" / "светлая тема" | Windows theme |
|
||||
| "Заблокируй компьютер" | LockWorkStation |
|
||||
| "Выключи компьютер" / "перезагрузи" / "ребутни" / "сон" / "гибернация" / "выйди из системы" / "отмени выключение" | Power management |
|
||||
| "Выключи компьютер через 30 минут" | Sleep timer для системы |
|
||||
| "Отмени таймер выключения" | shutdown /a + scheduler clear |
|
||||
|
||||
## Файлы / буфер / заметки
|
||||
|
||||
| Команда | Что делает |
|
||||
|---|---|
|
||||
| "Найди файл report" / "поищи documents" | File search |
|
||||
| "Запиши заметку купить хлеб" | Append в notes file |
|
||||
| "Покажи заметки" / "что я записал" | Открывает notes |
|
||||
| "Прочитай буфер" / "что в буфере" | Clipboard contents speak |
|
||||
| "Переведи буфер на английский" | LLM перевод + replace clipboard |
|
||||
|
||||
## Информация из интернета
|
||||
|
||||
| Команда | Что делает |
|
||||
|---|---|
|
||||
| "Какая погода" / "погода завтра" / "что с погодой" | Weather pack |
|
||||
| "Курс доллара" / "курс юаня" | CBR rates |
|
||||
| "Сколько 1000 долларов в рублях" | Currency convert |
|
||||
| "Сколько Сбер" / "сколько Газпром" | MOEX котировки (22 тикера) |
|
||||
| "Сколько биткоин" / "сколько эфир" | Crypto |
|
||||
| "Новости" / "что нового в мире" | News RSS |
|
||||
| "Найди в гугле X" / "загугли X" | DuckDuckGo Instant + LLM |
|
||||
| "Википедия X" / "что такое X" / "кто такой Y" | Wikipedia REST |
|
||||
| "Открыть Google поиск X" | Websearch (open browser) |
|
||||
| "Переведи на английский X" | Translate |
|
||||
|
||||
## Утилиты-калькуляторы
|
||||
|
||||
| Команда | Что делает |
|
||||
|---|---|
|
||||
| "Посчитай 2 плюс 2" / "сколько будет 10 в степени 3" | Math |
|
||||
| "Переведи 100 метров в футы" | Length convert |
|
||||
| "70 кг в фунты" | Weight convert |
|
||||
| "100 цельсий в фаренгейт" | Temperature convert |
|
||||
| "100 км в час в мили в час" | Speed convert |
|
||||
| "Сколько дней до нового года" | Date math (8 марта, 9 мая, Рождество, Новый год + любая дата) |
|
||||
| "Какой сегодня день недели" | Zeller's congruence |
|
||||
| "Время" / "сколько времени" / "какое сегодня число" | Date query |
|
||||
| "Подбрось монету" / "орёл или решка" | Coin flip |
|
||||
| "Сгенерируй пароль 16 символов" | Secure password → clipboard |
|
||||
| "Сгенерируй UUID" | v4 UUID → clipboard |
|
||||
| "Брось кубик" / "брось два кубика" | Dice |
|
||||
| "Выбери случайно из X или Y или Z" | Random choice |
|
||||
| "Запусти секундомер" / "сколько прошло" / "стоп секундомер" | Stopwatch |
|
||||
| "Прибавь счётчик" / "сколько на счётчике" / "сбрось счётчик" | Counter |
|
||||
| "Как пишется X" | Spelling |
|
||||
|
||||
## Разработка
|
||||
|
||||
| Команда | Что делает |
|
||||
|---|---|
|
||||
| "Укажи проект C:\..." | Codebase Q&A: установить root |
|
||||
| "Какой проект сейчас" | Текущий root |
|
||||
| "Что делает функция X" / "найди в коде Y" / "объясни код" | Walks codebase → LLM |
|
||||
| "Текущий репо owner/repo" | GitHub: установить repo |
|
||||
| "Какие пиары" / "открытые пиары" | List PRs via gh CLI |
|
||||
| "Разбери последний пиар" | LLM-ревью PR (gh pr view) |
|
||||
| "Сгенерируй код для X" | Code generation pack |
|
||||
| "Открой Cursor" / "VS Code" | apps/ |
|
||||
|
||||
## Развлечения
|
||||
|
||||
| Команда | Что делает |
|
||||
|---|---|
|
||||
| "Расскажи анекдот" / "пошути" / "скажи фразу" / "тостер" | Fun pack (jokes) |
|
||||
| "Запиши настроение 7" / "сегодня мне грустно" | Mood log |
|
||||
| "Как прошла неделя" / "сводка настроения" | Mood recap via LLM |
|
||||
| "Запусти Steam игру X" / "поиграй в Y" | Games pack |
|
||||
|
||||
## Скриншоты / экран
|
||||
|
||||
| Команда | Что делает |
|
||||
|---|---|
|
||||
| "Что на экране" / "опиши экран" | Vision LLM описание |
|
||||
| "Прочитай ошибку" | Vision LLM: ищет stack trace, объясняет |
|
||||
| "Сделай скриншот" / "скрин в буфер" | Screenshot → clipboard |
|
||||
| "Сохрани скриншот" | Screenshot → ~/Pictures/Screenshots/ |
|
||||
| "OCR экрана" / "прочитай текст" | OCR (existing pack) |
|
||||
| "Суммаризируй экран" | Summarize visible window |
|
||||
|
||||
---
|
||||
|
||||
## Как добавить свою команду
|
||||
|
||||
Скопируй `resources/commands/echo/` как шаблон, отредактируй `command.toml`
|
||||
(id, phrases) и `*.lua` (логика). Рекомендуемая структура — см.
|
||||
[ARCHITECTURE.md → Recommended pack structure](ARCHITECTURE.md#recommended-pack-structure).
|
||||
|
||||
Минимум:
|
||||
|
||||
```lua
|
||||
-- thing.lua
|
||||
local phrase = (jarvis.context.phrase or ""):lower()
|
||||
local body = jarvis.text.strip_trigger(phrase, { "сделай штуку" })
|
||||
body = body:gsub("^[%s,:%.]+", ""):gsub("%s+$", "")
|
||||
|
||||
if body == "" then
|
||||
return jarvis.cmd.error("Что именно?")
|
||||
end
|
||||
|
||||
-- ... do work ...
|
||||
|
||||
return jarvis.cmd.ok("Готово.")
|
||||
```
|
||||
|
||||
Затем `cargo test -p jarvis-core --lib commands::tests` — он автоматически
|
||||
проверит что TOML парсится и Lua-скрипт существует.
|
||||
|
||||
## Голосовое vs GUI
|
||||
|
||||
GUI страницы — для случаев когда голосом неудобно:
|
||||
|
||||
- `/commands` — все 67 паков с поиском (для discovery)
|
||||
- `/macros` — список с кнопкой "Запустить"
|
||||
- `/scheduler` — задачи с auto-poll каждые 5 сек
|
||||
- `/memory` — добавление/удаление фактов с фильтром
|
||||
- `/settings → AI Backends` — переключение LLM/TTS через dropdown
|
||||
|
||||
## Что-то не работает?
|
||||
|
||||
1. **Голос не распознаётся** — проверь микрофон в Settings → Devices. Скажи
|
||||
"повтори за мной привет" — если эхо проходит, проблема не в STT.
|
||||
2. **Не слышу ответ** — проверь TTS backend в Settings → AI Backends. Скажи
|
||||
"проверка эхо" — должен быть слышен голос. Если нет, проверь динамики +
|
||||
попробуй `JARVIS_TTS=sapi`.
|
||||
3. **"Не могу связаться с сервером"** — LLM unreachable. Если Groq —
|
||||
проверь GROQ_TOKEN и интернет. Если Ollama — `ollama serve` запущен?
|
||||
4. **Команда не найдена** — посмотри `/commands` в GUI, поищи фразу. Если
|
||||
фразы похожи но не та — agentic router сделает попытку, или подскажет.
|
||||
5. **Что-то странное в логах** — `<APP_LOG_DIR>/jarvis.log` (путь видно
|
||||
в первых строках `jarvis-app` stdout).
|
||||
38
resources/commands/interesting_fact/command.toml
Normal file
38
resources/commands/interesting_fact/command.toml
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Interesting fact about X via LLM.
|
||||
|
||||
[[commands]]
|
||||
id = "fact.about"
|
||||
type = "lua"
|
||||
script = "fact.lua"
|
||||
sandbox = "full"
|
||||
timeout = 15000
|
||||
|
||||
[commands.phrases]
|
||||
ru = [
|
||||
"расскажи интересный факт",
|
||||
"интересный факт о",
|
||||
"интересный факт про",
|
||||
"удиви меня",
|
||||
"интересный факт",
|
||||
"расскажи факт",
|
||||
]
|
||||
en = ["tell me an interesting fact", "fun fact about", "surprise me"]
|
||||
ua = ["цікавий факт"]
|
||||
|
||||
|
||||
[[commands]]
|
||||
id = "fact.history_today"
|
||||
type = "lua"
|
||||
script = "history_today.lua"
|
||||
sandbox = "full"
|
||||
timeout = 15000
|
||||
|
||||
[commands.phrases]
|
||||
ru = [
|
||||
"что было в этот день",
|
||||
"что произошло в этот день",
|
||||
"этот день в истории",
|
||||
"что случилось",
|
||||
]
|
||||
en = ["this day in history", "what happened today"]
|
||||
ua = ["що сталося в цей день"]
|
||||
35
resources/commands/interesting_fact/fact.lua
Normal file
35
resources/commands/interesting_fact/fact.lua
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
-- "Интересный факт о космосе" / "удиви меня"
|
||||
local phrase = (jarvis.context.phrase or ""):lower()
|
||||
local topic = jarvis.text.strip_trigger(phrase, {
|
||||
"расскажи интересный факт про",
|
||||
"расскажи интересный факт о",
|
||||
"интересный факт про",
|
||||
"интересный факт о",
|
||||
"расскажи интересный факт",
|
||||
"интересный факт",
|
||||
"расскажи факт",
|
||||
"удиви меня",
|
||||
"tell me an interesting fact",
|
||||
"fun fact about",
|
||||
"surprise me",
|
||||
"цікавий факт",
|
||||
})
|
||||
topic = topic:gsub("^[%s,:%.]+", ""):gsub("%s+$", "")
|
||||
|
||||
local user_prompt
|
||||
if topic == "" then
|
||||
user_prompt = "Расскажи один реально неочевидный научно-проверенный факт. На русском, 1-2 предложения. Без вступлений типа 'знали ли вы'."
|
||||
else
|
||||
user_prompt = "Расскажи один интересный факт про " .. topic .. ". На русском, 1-2 предложения. Без вступлений."
|
||||
end
|
||||
|
||||
local reply = jarvis.llm({
|
||||
{ role = "system", content = "Ты любопытный собеседник. Говоришь короткими, цепляющими фактами без воды." },
|
||||
{ role = "user", content = user_prompt }
|
||||
}, { max_tokens = 200, temperature = 0.8 })
|
||||
|
||||
if not reply or reply == "" then
|
||||
return jarvis.cmd.error("Сегодня нет интересных фактов.")
|
||||
end
|
||||
|
||||
return jarvis.cmd.ok(reply)
|
||||
48
resources/commands/interesting_fact/history_today.lua
Normal file
48
resources/commands/interesting_fact/history_today.lua
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
-- "Что было в этот день в истории" — Wikipedia API + LLM сжатие.
|
||||
local t = jarvis.context.time
|
||||
|
||||
-- Wikipedia's "On this day" Featured Content API:
|
||||
-- https://api.wikimedia.org/feed/v1/wikipedia/ru/onthisday/events/MM/DD
|
||||
local url = string.format(
|
||||
"https://api.wikimedia.org/feed/v1/wikipedia/ru/onthisday/events/%02d/%02d",
|
||||
t.month, t.day
|
||||
)
|
||||
|
||||
local data = jarvis.http.json(url)
|
||||
if not data or not data.events or #data.events == 0 then
|
||||
-- fallback: EN
|
||||
url = string.format(
|
||||
"https://api.wikimedia.org/feed/v1/wikipedia/en/onthisday/events/%02d/%02d",
|
||||
t.month, t.day
|
||||
)
|
||||
data = jarvis.http.json(url)
|
||||
end
|
||||
|
||||
if not data or not data.events or #data.events == 0 then
|
||||
return jarvis.cmd.not_found("На этот день в истории ничего интересного.")
|
||||
end
|
||||
|
||||
-- Pick first 5 events to feed into LLM for summarisation
|
||||
local sample = {}
|
||||
for i = 1, math.min(5, #data.events) do
|
||||
local ev = data.events[i]
|
||||
local year = ev.year or "?"
|
||||
local text = ev.text or ""
|
||||
if text ~= "" then
|
||||
table.insert(sample, year .. ": " .. text)
|
||||
end
|
||||
end
|
||||
|
||||
local context = table.concat(sample, "\n")
|
||||
|
||||
local reply = jarvis.llm({
|
||||
{ role = "system", content = "Ты — историк. Из списка событий выбери 2-3 самых интересных для русскоязычного слушателя, расскажи коротко на русском. Без преамбул." },
|
||||
{ role = "user", content = "Сегодня " .. t.day .. " число месяца " .. t.month .. ". События в этот день:\n\n" .. context }
|
||||
}, { max_tokens = 350, temperature = 0.4 })
|
||||
|
||||
if not reply or reply == "" then
|
||||
-- Fallback: just speak first event
|
||||
return jarvis.cmd.ok(sample[1] or "Не нашёл интересного.")
|
||||
end
|
||||
|
||||
return jarvis.cmd.ok(reply)
|
||||
18
resources/commands/mailto/command.toml
Normal file
18
resources/commands/mailto/command.toml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Mailto: open default mail client to compose a new message.
|
||||
|
||||
[[commands]]
|
||||
id = "mailto.compose"
|
||||
type = "lua"
|
||||
script = "compose.lua"
|
||||
sandbox = "full"
|
||||
timeout = 5000
|
||||
|
||||
[commands.phrases]
|
||||
ru = [
|
||||
"напиши письмо",
|
||||
"новое письмо",
|
||||
"открой почту",
|
||||
"составить письмо",
|
||||
]
|
||||
en = ["compose email", "new email", "open mail"]
|
||||
ua = ["напиши лист", "новий лист"]
|
||||
32
resources/commands/mailto/compose.lua
Normal file
32
resources/commands/mailto/compose.lua
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
-- "Напиши письмо имя_контакта" → opens mailto: URI in default handler.
|
||||
-- For now just opens a blank compose; could be extended to pull recipient
|
||||
-- from memory ("маме", "коллеге") via lookups.
|
||||
local phrase = (jarvis.context.phrase or ""):lower()
|
||||
local target = jarvis.text.strip_trigger(phrase, {
|
||||
"напиши письмо",
|
||||
"новое письмо",
|
||||
"открой почту",
|
||||
"составить письмо",
|
||||
"compose email",
|
||||
"new email",
|
||||
"open mail",
|
||||
})
|
||||
target = target:gsub("^[%s,:%.]+", ""):gsub("%s+$", "")
|
||||
|
||||
local recipient = ""
|
||||
if target ~= "" then
|
||||
-- Try memory lookup — "напиши письмо маме" → memory.recall("email_маме")
|
||||
local key = "email_" .. target
|
||||
local addr = jarvis.memory.recall(key)
|
||||
if addr and addr ~= "" then
|
||||
recipient = addr
|
||||
end
|
||||
end
|
||||
|
||||
local uri = recipient ~= "" and ("mailto:" .. recipient) or "mailto:"
|
||||
jarvis.system.open(uri)
|
||||
|
||||
if recipient ~= "" then
|
||||
return jarvis.cmd.ok("Открываю письмо для " .. target .. ".")
|
||||
end
|
||||
return jarvis.cmd.ok("Открываю почтовый клиент.")
|
||||
Loading…
Add table
Add a link
Reference in a new issue