Scripting Basics
Rune is configured in Lua. There is no separate trigger syntax or settings file. One script runs at startup, and everything is registered from it:
~/.config/rune/init.luaThe edit/reload loop
Section titled “The edit/reload loop”Edit init.lua, type /reload, keep playing.
/reload throws away the Lua state and runs your scripts again from
scratch. A script error doesn’t take the client down: it’s reported with
its file:line, and the rest of your config loads normally. Fix the line
and /reload again.
Two commands are useful while iterating:
/lua <code>runs a one-liner without touching a file:/lua rune.echo(rune.state.address)/aliases,/triggers,/timers,/hooks, and/bindslist what’s registered, including thefile:linethat registered it
A first script
Section titled “A first script”-- ~/.config/rune/init.lua
-- An alias: "hp" sends the heal commandrune.alias.exact("hp", "cast 'heal' self")
-- A trigger: highlight incoming hits by rewriting the linerune.trigger.contains("You are hit", function(matches, ctx) return rune.style.red(ctx.line:clean())end)
-- A timer: save every minuterune.timer.every(60, "save", { name = "autosave" })Save, then /reload.
Growing past one file
Section titled “Growing past one file”When init.lua gets long, split it up with require(). Paths resolve
relative to the requiring script, so files next to init.lua load without
any path setup:
~/.config/rune/├── init.lua├── combat.lua└── ui.lua-- init.luarequire("combat") -- runs combat.luarequire("ui") -- runs ui.luaA required file is plain Lua that runs top to bottom. Put registrations in
it directly; no module table or return is needed:
-- combat.luarune.alias.exact("k", "kill target")rune.trigger.contains("flees in panic", "kill target")Since /reload rebuilds the whole Lua state, edits to required files are
loaded on /reload, along with init.lua.
Customizing built-in behavior
Section titled “Customizing built-in behavior”The input hooks, local echo, default keymap, and status bar are Lua scripts
embedded in the binary. They use the same registration functions as your
init.lua. For example, replace the default > prefix on echoed commands
with an echo hook:
rune.hooks.on("echo", function(text) return rune.style.cyan("» " .. text)end, { priority = 50 }) -- runs before the default handlerAnything the client does through this API, your scripts can override.
A map of the API
Section titled “A map of the API”Everything lives under the rune table. Find the task, follow the link:
| To do this | Guide | Full signatures |
|---|---|---|
| React to server output | Triggers | rune.trigger |
| Shorten commands you type | Aliases | rune.alias |
| Run something later, or on a schedule | Timers | rune.timer |
| Intercept input, output, and client events | Hooks & Events | rune.hooks |
| Bind keys | Key Bindings | rune.bind |
Add your own /commands |
Custom Commands | rune.command |
| Handle GMCP data | GMCP | rune.gmcp |
| Keep data across reloads or restarts | Storage & Worlds | Storage |
| Toggle sets of things at once | Groups | rune.group |
| Lay out panes, bars, and pickers | Layout & UI | rune.ui, rune.pane |
| Log the session to a file | Logging | rune.log |
| Color and style text | rune.style |
The registration functions all behave the same way — handles, a shared
options table, groups, and error quarantine.
The Scripting Model covers that machinery once, and
the API reference has full signatures for every
rune.* namespace.
Read Triggers for pattern matching and The Scripting Model for registration options and handles. For TinTin++ and Mudlet equivalents, see Migrating from Other Clients.