# Your First MOO Verb This tutorial walks you through creating, editing, and testing a MOO verb from scratch. By the end you'll have written working Python code that runs inside the MOO, know how to send output to players, and know how to accept command arguments. ## Prerequisites Before you start: - A running DjangoMOO development environment (see {doc}`../how-to/development`) - An SSH connection to the server as the Wizard user To connect, run: ```bash ssh -p 8022 Wizard@localhost ``` ## Step 1: Orient yourself Type `look` and press Enter. You'll see whatever room your wizard starts in — for the default bootstrap, it's The Laboratory: ``` ↖ ↑ ↗ The Laboratory ← ↕ → A cavernous laboratory filled with gadgetry of every kind, this seems ↙ ↓ ↘ like a dumping ground for every piece of dusty forgotten equipment a mad scientist might require. You see a heavy wooden workbench here. Newman, Cliff, and Player are here. ``` Type `@who` to confirm you're connected as Wizard: ``` Connected players: Wizard [The Laboratory] ``` As Wizard you can create and edit verbs anywhere. ## Step 2: Create and edit a verb MOO verbs live on objects. The simplest place to put your first verb is on yourself — the Wizard object. Run: ``` @edit verb greet on me ``` This creates a new verb named `greet` on the Wizard object and opens the built-in editor. The status bar at the bottom shows the available keybindings: ``` [Ctrl+S] Save [Ctrl+C/Q] Cancel ``` Type this as the verb code: ```python #!moo verb greet --on Wizard print("Hello, world!") ``` The first line is a *shebang* — it tells DjangoMOO this is a verb named `greet` attached to the `Wizard` object. Every verb starts with one. We'll add more flags to it in Step 4. Press `Ctrl+S`, then `Y` to confirm the save. The editor closes and you're back at the prompt. (For scripted use, `@edit verb` also accepts a `with ""` argument that bypasses the editor — useful for automation but harder to read for multi-line code.) ## Step 3: Test it Type `greet`: ``` $ greet Hello, world! ``` You just ran your first MOO verb. `print()` sends output back to you, the caller, buffered until the verb finishes. ## Step 4: Accept an argument Let's update `greet` to accept a name. Open the editor again: ``` @edit verb greet on me ``` Replace the code with: ```python #!moo verb greet --on Wizard --dspec any from moo.sdk import context name = context.parser.get_dobj_str() print(f"Hello, {name}!") ``` Save with `Ctrl+S` → `Y`. We've added `--dspec any` to the shebang. That tells the parser this verb requires a direct object. When the verb fires, `context.parser.get_dobj_str()` returns whatever the player typed after the verb name. If the player types `greet` with nothing after it, the parser refuses to dispatch the verb at all. `--on` accepts a few forms: a player name (`Wizard`), an object ID (`#5`), or a system property (`$thing`, `$room`). When editing interactively, the verb stays on whatever object the `@edit verb ... on ` command attached it to. Test it: ``` $ greet world Hello, world! ``` ``` $ greet The verb "greet" requires a direct object. ``` The second response comes from the parser itself, not your code, because `--dspec any` prevented dispatch. ## What just happened **`context.parser`** is the parser instance for the current command. `get_dobj_str()` returns the direct object as a plain string — whatever the player typed after the verb name. It raises `NoSuchObjectError` if nothing was typed, so when `--dspec any` is in place you can call it unconditionally. Only catch the exception if you want to provide a custom message or fallback behavior. **`from moo.sdk import context`** brings the per-task context object into your verb scope. It's the standard way verbs access the parser, the player, the caller, and other ambient state. Most non-trivial verbs need it. **`print()`** sends a line of text to the player who ran the command. For output to other players in the room, use `context.player.location.announce_all_but(context.player, msg)` instead. **Return values from player-typed commands are discarded.** Whatever this verb returns when invoked via the parser goes nowhere — use `print()` for player-visible output. Verbs called from other verb code can and do return values; that's how a lot of the standard library works. **The shebang line** is how verb dispatch properties are set. `--on` names the object the verb lives on. When editing interactively, the verb stays wherever `@edit verb ... on ` put it; `--on` in the shebang doesn't move it. `--dspec any` means a direct object must be present. `--dspec either` makes it optional. Omitting `--dspec` (or `--dspec none`) means the verb only matches when no direct object is given. See {doc}`../how-to/creating-verbs` for the full shebang syntax including `--ispec` for indirect objects. ## Step 5: Make it permanent (optional) Verbs created with `@edit verb` live in the database and survive server restarts, but it won't be part of other deployments using the `default` dataset. If you want this verb to be part of your bootstrap dataset, add a verb file in `default/verbs/`. See {doc}`../reference/bootstrapping` for how that works. ## Where to go next - {doc}`../how-to/creating-verbs` — Complete guide to verb code format, the full shebang syntax, parser API, error handling, and output mechanisms - {doc}`../how-to/advanced-verbs` — Calling other verbs, async patterns, time-limited tasks, SDK functions - {doc}`../reference/runtime` — The `context` variable and all its attributes - {doc}`../reference/parser` — Full reference for parser methods: `get_pobj_str()`, `has_dobj_str()`, and more