SDK Functions

Every name listed here is exported from moo.sdk and importable in verb code (from moo.sdk import lookup, create, ...). For methods defined on the Object model and accessible as obj.<name>(...), see Objects in the DjangoMOO Database. For Property and Verb field reference, see Properties on Objects and Verbs on Objects.

Object lifecycle and lookup

moo.sdk.lookup(x, return_first=True)

Lookup an object within the current site by PK, name, or alias.

PK lookups are site-scoped: an integer PK that belongs to a different universe raises NoSuchObjectError the same as a missing object would. Internal system code that genuinely needs cross-site access should use Object.global_objects directly rather than going through this function.

Parameters:
  • x (Union[int, str]) – lookup value

  • return_first (bool) – when True (default), return the first match or raise NoSuchObjectError; when False, return a list of all matches (may be empty)

Returns:

the result of the lookup, or a list when return_first is False

Return type:

Object | list[Object]

Raises:

NoSuchObjectError – when a result cannot be found and return_first is True

moo.sdk.create(name, *a, **kw)

Creates and returns a new object whose parents are parents and whose owner is as described below. Provided parents are valid Objects with derive permission, otherwise PermissionError is raised. After the new object is created, its initialize verb, if any, is called with no arguments.

The owner of the new object is either the programmer (if owner is not provided), or the provided owner, if the caller has permission to entrust the object.

If the intended owner of the new object has a property named ownership_quota and the value of that property is an integer, then create() treats that value as a quota. If the quota is less than or equal to zero, then the quota is considered to be exhausted and create() raises QuotaError instead of creating an object. Otherwise, the quota is decremented and stored back into the ownership_quota property as a part of the creation of the new object.

Parameters:
  • name (str) – canonical name

  • owner (Object) – owner of the Object being created

  • location (Object) – where to create the Object

  • parents (list[Object]) – a list of parents for the Object

  • obvious (bool) – whether the object appears in room contents listings (default False)

Returns:

the new object

Return type:

Object

Raises:
  • PermissionError – if the caller is not allowed to derive from the parent

  • QuotaError – if the caller has a quota and it has been exceeded

moo.sdk.players()

Return a list of all player avatar Objects.

Returns:

Objects that are player avatars

Return type:

list[Object]

moo.sdk.connected_players(within=None)

Return a list of player avatars whose last_connected_time property was updated within the given within window (default: 5 minutes).

The last_connected_time value is precached into the session-level property cache on every returned Object so subsequent get_property calls incur no extra queries.

Parameters:

within (timedelta) – recency window; defaults to timedelta(minutes=5)

Returns:

Objects whose avatars have connected recently

Return type:

list[Object]

moo.sdk.owned_objects(player_obj)

Return a QuerySet of all Objects owned by player_obj, ordered by name.

Parameters:

player_obj – the owner Object

Return type:

QuerySet

moo.sdk.owned_objects_by_pks(pk_list)

Return a QuerySet of Objects with PKs in pk_list, ordered by name.

Used by continuation tasks (e.g. audit_batch) where the target player is not in scope but the remaining PK list was passed as args[0].

Parameters:

pk_list – list of integer Object PKs

Return type:

QuerySet

moo.sdk.ensure_player_record(obj)

Ensure a Player row exists for obj with user=None.

Used by NPC initialization so the avatar reports is_player() == True to the parser while is_connected() == False causes tell() to silently drop (no connection). Idempotent: returns the existing Player if one already references this avatar.

Parameters:

obj – the avatar Object

Returns:

the Player row (created or pre-existing)

Raises:

UserError – if the current caller is not a wizard

moo.sdk.remove_player_record(obj)

Delete any anonymous (user=None) Player rows pointing at obj.

Counterpart to ensure_player_record(); used by the $npc.recycle verb. Rows tied to a real User are left alone — those belong to a human player and removing them is not this helper’s job.

Parameters:

obj – the avatar Object

Returns:

number of Player rows deleted

Raises:

UserError – if the current caller is not a wizard

Tasks and continuations

moo.sdk.invoke(*args, verb=None, callback=None, delay=0, periodic=False, cron=None, _caller=None, _player=None, **kwargs)

Asynchronously execute a Verb, optionally returning the result to another Verb. This is often a better alternative than using __call__-syntax to invoke a verb directly, since Verbs invoked this way will each have their own timeout.

Parameters:
  • verb (Verb) – the Verb to execute

  • callback (Verb) – an optional callback Verb to receive the result

  • delay (int) – seconds to wait before executing, cannot be used with cron

  • periodic (bool) – should this task continue to repeat? cannot be used with cron

  • cron (str | None) – a crontab expression to schedule Verb execution

  • _caller – explicit caller override; falls back to context.caller. Used by transaction.on_commit callbacks that fire after the ContextManager has exited and cleared the contextvars.

  • _player – explicit player override; falls back to context.player. Same reason as _caller.

  • args – positional arguments for the Verb, if any

  • kwargs – keyword arguments for the Verb, if any

Returns:

a PeriodicTask instance or None if the task is a one-shot

Return type:

Optional[PeriodicTask]

moo.sdk.cancel_scheduled_task(pk)

Delete a django_celery_beat.PeriodicTask row by primary key.

Used by daemon lifecycle verbs ($daemon.disable, $daemon.recycle) to remove the PT created by invoke() (periodic=True). Idempotent: returns False if no PT with that pk exists.

Parameters:

pk (int) – the PeriodicTask.pk returned by invoke()

Return type:

bool

Returns:

True if a row was deleted, False if not found

Raises:

UserError – if the current caller is not a wizard

moo.sdk.get_scheduled_task_info(pk)

Look up runtime stats for a scheduled task created by invoke().

Returns None if no PeriodicTask exists with the given pk (useful for orphan-pointer detection on daemon Objects).

Parameters:

pk (int) – the PeriodicTask.pk to inspect

Return type:

dict | None

Returns:

dict with enabled, last_run_at, total_run_count, interval_seconds (or None), and task (the Celery task name) — or None if not found.

Raises:

UserError – if the current caller is not a wizard

moo.sdk.task_time_low(threshold=0.5)

Return True if the current task’s remaining time is at or below threshold seconds.

Always returns False when there is no task-time limit (e.g. in tests or interactive shells without a configured limit).

Parameters:

threshold – seconds remaining before considering time low (default 0.5)

Return type:

bool

moo.sdk.schedule_continuation(remaining_items, verb, msg=None)

Schedule a continuation task carrying the PKs of remaining_items and notify the current player.

Intended for use inside long-running verbs that iterate over many objects. The continuation verb (e.g. audit_batch, reload_batch) receives args[0] as the list of PKs and dispatches on verb_name.

Usage:

for i, item in enumerate(items):
    if task_time_low():
        schedule_continuation(items[i:], this.get_verb("audit_batch"))
        return
    # ... process item
Parameters:
  • remaining_items – iterable of Objects (or any model with .pk)

  • verb – Verb instance to invoke for the continuation

  • msg – optional override for the progress message shown to the player

moo.sdk.set_task_perms(who)

Set the task permissions to those of who for the duration of the with-block. :type who: :param who: the Object whose permissions to assume :type who: Object

moo.sdk.moo_eval(code_string)

Evaluate arbitrary Python code in the RestrictedPython sandbox.

The code runs with the same environment as verb code, with standard verb variables (this, _, context) automatically available.

Parameters:

code_string (str) – Python code to evaluate

Returns:

The result of the evaluation

moo.sdk.invoked_verb_name(default=None)

Return the verb name as the player typed it (lowercased), or default when there is no active parser context.

Verbs called via moo.sdk.invoke(), async callbacks, or test harnesses have no parser; pass the sandbox-injected verb_name as default to fall back to the verb’s defined name.

Return type:

str | None

Output and full-screen UIs

moo.sdk.write(obj, message)

Send an asynchronous message to the user.

Parameters:
  • obj (Object) – the Object to write to

  • message (Any) – any pickle-able object

moo.sdk.open_editor(obj, initial_content, callback_verb, *args, content_type='text', title=None)

Request the connected SSH client to open a full-screen text editor. When the user saves, the edited text is passed to callback_verb as args[0], followed by any extra positional arguments supplied here. If the user cancels, the callback is not invoked.

Parameters:
  • obj – the player Object whose client should open the editor

  • initial_content (str) – text to pre-populate the editor buffer

  • callback_verb – Verb to invoke with the edited text as args[0]

  • args – additional arguments forwarded to the callback verb as args[1:]

  • content_type (str) – “python”, “json”, or “text” (default); controls syntax highlighting

moo.sdk.open_paginator(obj, content, content_type='text')

Request the connected SSH client to open a full-screen read-only paginator. The user can scroll through the content and press Q to quit.

In raw-mode sessions (MUD clients, line-oriented terminals) the SSH server intercepts the paginator event and writes the content directly to the terminal instead of opening the interactive UI.

Parameters:
  • obj – the player Object whose client should open the paginator

  • content (str) – text to display

  • content_type (str) – “python”, “json”, or “text” (default); controls syntax highlighting

moo.sdk.can_open_editor()

True if the current player’s client can display an editor.

Either the player is in rich mode (prompt_toolkit TUI), or their client advertises support for the GMCP Editor package via Core.Supports.Set (the djangomoo Mudlet bridge does this; the server hands the edit off to the client’s preferred local editor over GMCP). Verbs that open the editor should gate on this rather than get_client_mode() == "raw" so bridge-equipped MUD clients are not forced onto the inline with "..." fallback.

Return type:

bool

Session settings and client capabilities

moo.sdk.get_client_mode()

Return the current player’s shell mode.

Returns "rich" (prompt_toolkit TUI, the default) or "raw" (line- based I/O for traditional MUD clients that cannot handle cursor control).

Verbs use this to short-circuit editor-opening code paths in raw mode and suggest the inline @edit ... with "..." form instead. Most verbs should call can_open_editor() instead, which also returns True for raw-mode clients that advertise GMCP Editor support (e.g. the djangomoo Mudlet bridge).

Return type:

str

moo.sdk.get_wrap_column()

Return the effective wrap column for the current player.

Reads the player’s wrap_column property. If it is "auto" (or the property is not set), returns the terminal_width session setting, falling back to 80 if the terminal width is not known.

moo.sdk.get_session_setting(key, default=None)

Get a session-specific output setting for the current player.

Session settings are stored per-user and cleared on disconnect. Used by PREFIX, SUFFIX, OUTPUTPREFIX/SUFFIX, and the a11y verb.

Checks the in-process _session_settings dict first (authoritative in the SSH server process and in tests), then falls back to the Django cache so Celery workers — which run in a separate process — can also read the value.

Parameters:
  • key – setting name (‘output_prefix’, ‘output_suffix’, ‘quiet_mode’, ‘color_system’)

  • default – value to return if setting is not found

Returns:

setting value or default

moo.sdk.set_session_setting(key, value)

Set a session-specific output setting for the current player.

Session settings are stored per-user and cleared on disconnect. Used by PREFIX, SUFFIX, OUTPUTPREFIX/SUFFIX, and the a11y verb.

Writes to the Django cache (accessible cross-process from Celery workers) and also publishes a session_setting event to the player’s Kombu queue so the SSH server’s process_messages() loop can update its own registry.

Parameters:
  • key – setting name (‘output_prefix’, ‘output_suffix’, ‘quiet_mode’, ‘color_system’)

  • value – setting value

Out-of-band MUD-client protocols

moo.sdk.send_gmcp(obj, module, data=None)

Send a GMCP event to obj’s SSH channel.

GMCP (Generic MUD Communication Protocol) is the canonical OOB channel for structured MUD events. Clients that negotiated GMCP receive IAC SB GMCP <module> <json> IAC SE; clients that did not see it as zero bytes on the wire (the SSH server skips the emit if the capability flag is false).

Example:

send_gmcp(player, "Char.Vitals", {"hp": 50, "maxhp": 100})
send_gmcp(player, "Room.Info", {"num": 12, "name": "A dim hall"})
send_gmcp(player, "Core.Ping")  # no payload
Parameters:
  • obj – the Object (player avatar) to send the GMCP event to

  • module (str) – GMCP module/package name, e.g. "Char.Vitals"

  • data – JSON-serializable value, or None for an empty event

moo.sdk.play_sound(obj, name, volume=100, priority=10)

Play a sound on obj’s client.

Prefers GMCP Client.Media.Play when the client negotiated GMCP; falls back to the inline MSP !!SOUND(...) marker if the client negotiated MSP; no-ops otherwise.

DjangoMOO does not bundle any sound assets. Sound pack authors are expected to provide filenames that their client-side pack can resolve.

Parameters:
  • obj – the player to play the sound on

  • name (str) – filename (client-resolvable), e.g. "door.wav"

  • volume (int) – 0–100

  • priority (int) – higher numbers preempt lower-priority sounds

moo.sdk.room_info_payload(room)

Build the IRE-style GMCP Room.Info payload for room.

Returns {"num", "name", "exits"} with values stringified per the Achaea/Aardwolf/Mudlet-generic-mapper convention. Exit keys are normalized to short codes (n, ne, u, …); custom-named exits ("ladder", "portal") round-trip unchanged so they still appear on the client.

DjangoMOO names cardinal exits "<direction> from <room>" — e.g. "east from grand foyer" — to keep them globally unique. We check the exit’s exact name, then its first word, then its aliases for a direction match before falling back to the raw name.

Parameters:

room – an Object representing a room

Return type:

dict

Returns:

a dict suitable for send_gmcp(player, "Room.Info", payload)

Server administration

moo.sdk.boot_player(obj)

Disconnect the given player from the MOO server.

Publishes a disconnect event to the player’s Kombu message queue, which the SSH server’s process_messages() loop picks up and exits cleanly.

Permission: the caller must be the player being booted, or a wizard.

Parameters:

obj – the player Object to disconnect

moo.sdk.server_info()

Return a dict with server version and process statistics.

Keys: version, python, pid, memory_mb (may be None on platforms where resource is unavailable).

Return type:

dict

Mail Functions

moo.sdk.send_message(sender, recipients, subject, body)

Create a message and deliver it to all recipients.

Parameters:
  • sender – the sending Object (must be a player)

  • recipients (list) – list of recipient Objects (must be players)

  • subject (str) – message subject line

  • body (str) – message body text

Returns:

the created Message instance

moo.sdk.get_mailbox(player, include_deleted=False)

Return player’s received messages as a list of MessageRecipient rows, newest first.

Parameters:
  • player – the recipient Object

  • include_deleted (bool) – if True, include soft-deleted messages

Return type:

list

Returns:

list of MessageRecipient instances

moo.sdk.get_message(player, n)

Return the nth message (1-based) in the player’s non-deleted mailbox, or None.

Parameters:
  • player – the recipient Object

  • n (int) – 1-based message index

Returns:

MessageRecipient or None

moo.sdk.mark_read(player, n)

Mark message n as read. Returns True if successful, False if n is out of range.

Return type:

bool

moo.sdk.delete_message(player, n)

Soft-delete message n. Returns True if successful, False if n is out of range.

Return type:

bool

moo.sdk.undelete_message(player, n)

Restore the nth deleted message (1-based among deleted-only list). Returns True if successful, False if n is out of range.

Return type:

bool

moo.sdk.count_unread(player)

Return the count of unread, non-deleted messages for the player.

Return type:

int

moo.sdk.get_mail_stats(player)

Return a dict with total, unread, and deleted message counts for the player. All counts are computed in two queries.

Return type:

dict