Simpler than AHK keyboard hooks: record what the user SAID, replay each phrase
through the normal dispatch path. Works for any command jarvis already knows.
Flow:
"Запиши макрос работа" → start_recording("работа")
"Открой браузер" → buffered into the recording
"Запусти спотифай" → buffered
"Режим работа" → buffered
"Сохрани макрос" → save_recording() → persist
...later...
"Запусти макрос работа" → replay("работа") → fires each phrase in order
Core (crates/jarvis-core/src/macros.rs)
- Macro {name, steps, created_at, last_run} persisted at
<APP_CONFIG_DIR>/macros.json with atomic write-through.
- Recording state in-memory: RwLock<Option<RecordingState>>.
- start_recording / record_step / save_recording / cancel_recording.
- list / get / delete / mark_run.
- replay(name) spawns a thread that fires each step through a callback,
with 800ms inter-step delay. Caller returns immediately.
- is_macro_control filter prevents recording the meta-commands themselves
(no infinite recursion when "запиши макрос" runs during a recording).
- 3 unit tests (55 total).
Wiring (crates/jarvis-app/src/main.rs)
- macros::init() before listener starts.
- macros::set_replay_callback(move |phrase| text_cmd_tx.send(phrase)) — each
replay step gets queued as a synthetic text command, processed by the same
dispatcher the GUI uses. No special-case code path.
Recording hook (crates/jarvis-app/src/app.rs::execute_command)
- On successful command execution → call macros::record_step(text).
- Failed commands are NOT recorded (would fail on replay too).
- is_macro_control filter inside record_step skips meta-commands.
Lua API (crates/jarvis-core/src/lua/api/macros.rs)
- jarvis.macros.{start, save, cancel, replay, list, delete,
is_recording, recording_name}
Voice pack (resources/commands/macros/, 6 commands)
- "запиши макрос NAME" → macros.start_recording
- "сохрани макрос" → macros.save
- "отмени макрос" → macros.cancel
- "запусти макрос NAME" → macros.replay
- "какие у меня макросы" → macros.list
- "удали макрос NAME" → macros.delete
Build: cargo build --release -p jarvis-app green. 55/55 tests pass.
70 lines
2.2 KiB
Rust
70 lines
2.2 KiB
Rust
//! Lua bindings for the macro recorder.
|
|
//!
|
|
//! Usage from Lua:
|
|
//! jarvis.macros.start("работа") -- begin recording
|
|
//! jarvis.macros.save() -- persist + stop
|
|
//! jarvis.macros.cancel() -- abort without saving
|
|
//! jarvis.macros.replay("работа") -- play back; returns step count or nil
|
|
//! jarvis.macros.list() -- array of {name, steps_count, last_run}
|
|
//! jarvis.macros.delete(name) -- removes
|
|
//! jarvis.macros.is_recording() -- bool
|
|
//! jarvis.macros.recording_name() -- string or nil
|
|
|
|
use mlua::{Lua, Table, Value};
|
|
|
|
use crate::macros;
|
|
|
|
pub fn register(lua: &Lua, jarvis: &Table) -> mlua::Result<()> {
|
|
let m = lua.create_table()?;
|
|
|
|
m.set("start", lua.create_function(|_, name: String| {
|
|
macros::start_recording(&name).map_err(mlua::Error::external)
|
|
})?)?;
|
|
|
|
m.set("save", lua.create_function(|_, ()| {
|
|
macros::save_recording().map_err(mlua::Error::external)
|
|
})?)?;
|
|
|
|
m.set("cancel", lua.create_function(|_, ()| {
|
|
Ok(macros::cancel_recording())
|
|
})?)?;
|
|
|
|
m.set("is_recording", lua.create_function(|_, ()| {
|
|
Ok(macros::is_recording())
|
|
})?)?;
|
|
|
|
m.set("recording_name", lua.create_function(|lua, ()| {
|
|
match macros::recording_name() {
|
|
Some(n) => Ok(Value::String(lua.create_string(n)?)),
|
|
None => Ok(Value::Nil),
|
|
}
|
|
})?)?;
|
|
|
|
m.set("replay", lua.create_function(|_, name: String| {
|
|
macros::replay(&name).map_err(mlua::Error::external)
|
|
})?)?;
|
|
|
|
m.set("delete", lua.create_function(|_, name: String| {
|
|
Ok(macros::delete(&name))
|
|
})?)?;
|
|
|
|
m.set("list", lua.create_function(|lua, ()| {
|
|
let arr = lua.create_table()?;
|
|
for (i, mac) in macros::list().iter().enumerate() {
|
|
let row = lua.create_table()?;
|
|
row.set("name", mac.name.clone())?;
|
|
row.set("steps_count", mac.steps.len())?;
|
|
row.set("created_at", mac.created_at)?;
|
|
let last: Value = match mac.last_run {
|
|
Some(ts) => Value::Integer(ts),
|
|
None => Value::Nil,
|
|
};
|
|
row.set("last_run", last)?;
|
|
arr.set(i + 1, row)?;
|
|
}
|
|
Ok(arr)
|
|
})?)?;
|
|
|
|
jarvis.set("macros", m)?;
|
|
Ok(())
|
|
}
|