egregore_bridge

EGREGORE BRIDGE – Matrix Application Service ghost user manager. Egregore VN Engine by Sigma / Stargazer / Vivian The Loopmother

Creates, manages, and controls ghost Matrix users for summoned egregores. Each egregore gets a Matrix identity like @_egregore_orion:secure-channel.net with their own display name, avatar, and ability to send messages.

Uses the AS (Application Service) token to act on behalf of ghost users via the Synapse Client-Server API.

@fire @skull THE WITCH GIVES HER DOLLS VOICES.

egregore_bridge.ghost_user_id(name)[source]

Build the fully qualified Matrix user ID for an egregore ghost.

Lowercases the egregore name and replaces spaces and hyphens with underscores, then wraps it with the EGREGORE_PREFIX localpart prefix and the module-level SERVER_NAME domain to produce an identity like @_egregore_orion:secure-channel.net. This is the canonical way the bridge addresses a ghost, so the same name always maps to the same Matrix user.

A pure string helper that touches no I/O. Called throughout EgregoreBridge (in register_ghost(), set_display_name(), set_avatar(), upload_avatar_from_disk(), join_room(), leave_room(), send_message(), and send_emote()) and also directly by message_processor/generate_and_send.py when it records the ghost uid for an interleaved egregore turn.

Parameters:

name (str) – Human-facing egregore name (any case, spaces, or hyphens).

Returns:

The full Matrix user ID, e.g. @_egregore_name:server.

Return type:

str

egregore_bridge.ghost_localpart(name)[source]

Build the Matrix localpart (username without the domain) for a ghost.

Mirrors ghost_user_id() but stops before the server suffix: lowercases the name, swaps spaces and hyphens for underscores, and prepends EGREGORE_PREFIX to yield a value like _egregore_orion. The localpart is what Synapse’s registration endpoint expects in its username field.

A pure string helper with no I/O. Called by EgregoreBridge.register_ghost() to derive the username it posts to /_matrix/client/v3/register; no other repo callers were found.

Parameters:

name (str) – Human-facing egregore name (any case, spaces, or hyphens).

Returns:

The bare localpart, e.g. _egregore_name.

Return type:

str

class egregore_bridge.EgregoreBridge(homeserver_url='https://secure-channel.net', as_token='ca6d2f1fd1c97dad285c328d333d9552f60bc5b5e0e16b48f839bcf24a043c73c', server_name='secure-channel.net')[source]

Bases: object

Manages egregore ghost users on Matrix via the Application Service API.

All methods use the AS token to impersonate ghost users through Synapse’s /_matrix/client/v3/ endpoints with ?user_id= parameter.

Parameters:
  • homeserver_url (str)

  • as_token (str)

  • server_name (str)

__init__(homeserver_url='https://secure-channel.net', as_token='ca6d2f1fd1c97dad285c328d333d9552f60bc5b5e0e16b48f839bcf24a043c73c', server_name='secure-channel.net')[source]

Initialise the bridge with Synapse connection settings.

Stores the homeserver URL, Application Service token, and server name used to build ghost user IDs and authenticate every request. Defaults are pulled from the module-level config constants (HOMESERVER_URL/AS_TOKEN/SERVER_NAME), which themselves read from environment variables. The aiohttp session is created lazily on first use, and _registered caches which ghost user IDs have already been registered this process to keep registration idempotent and cheap.

This is invoked once by get_bridge(), which constructs the module-level singleton; callers such as tools/summon_egregore.py, tools/compose_scene.py, tools/dismiss_egregore.py, and message_processor/generate_and_send.py reach the bridge through that singleton rather than instantiating it directly.

Parameters:
  • homeserver_url (str) – Base URL of the Synapse homeserver; a trailing slash is stripped so paths can be concatenated cleanly.

  • as_token (str) – Application Service bearer token used in the Authorization header to impersonate ghost users.

  • server_name (str) – Matrix server name (domain) used to form fully qualified ghost user IDs like @_egregore_x:server.

Return type:

None

async close()[source]

Close the underlying HTTP session and release its connections.

Shuts down the shared aiohttp.ClientSession created by _get_session() if one is open, freeing the connection pool. A subsequent call to _get_session() will transparently create a fresh session, so the bridge remains usable after closing.

No internal callers were found; this is a cleanup hook for shutdown code that owns the bridge singleton (e.g. service teardown).

Return type:

None

async register_ghost(name)[source]

Register an egregore ghost user with Synapse, idempotently.

Derives the localpart and full user ID via ghost_localpart() and ghost_user_id(), then short-circuits if the ID is already in the in-memory _registered cache. Otherwise it POSTs an m.login.application_service registration through _api() to /_matrix/client/v3/register. Both a 200 success and a 400 with errcode M_USER_IN_USE are treated as success and added to the cache, so re-registering an existing ghost is cheap and safe; any other status is logged as an error and reports failure. Logs an info line on first registration and a debug line when the user already existed.

Called by ensure_ghost() as the first step of fully provisioning a ghost; ensure_ghost in turn is invoked from tools/summon_egregore.py.

Parameters:

name (str) – Human-facing egregore name to register.

Returns:

True if the ghost is registered (now or already), False on an unexpected error response.

Return type:

bool

async set_display_name(name, display_name)[source]

Set the Matrix profile display name for a ghost user.

Resolves the ghost’s user ID with ghost_user_id() and PUTs the new displayname to /_matrix/client/v3/profile/{user_id}/displayname through _api(), impersonating the ghost via the as_user parameter so the change is attributed to that user rather than the Application Service.

Called by ensure_ghost(), which passes either an explicit display name or a title-cased form of the egregore name; no direct external callers were found.

Parameters:
  • name (str) – Human-facing egregore name identifying the ghost.

  • display_name (str) – The display name to publish on the ghost’s profile.

Returns:

True when Synapse returns 200, False otherwise.

Return type:

bool

async set_avatar(name, mxc_uri)[source]

Point a ghost user’s profile avatar at an already-uploaded MXC asset.

Resolves the ghost’s user ID via ghost_user_id() and PUTs the avatar_url to /_matrix/client/v3/profile/{user_id}/avatar_url through _api(), impersonating the ghost so the avatar belongs to it. The caller must supply a Matrix content URI (mxc://...), such as the one returned by upload_avatar_from_disk(); this method does no uploading itself.

Called by ensure_ghost() immediately after a successful avatar upload; no direct external callers were found.

Parameters:
  • name (str) – Human-facing egregore name identifying the ghost.

  • mxc_uri (str) – A mxc:// content URI for previously uploaded media.

Returns:

True when Synapse returns 200, False otherwise.

Return type:

bool

async upload_avatar_from_disk(name)[source]

Find an egregore’s avatar image on disk and upload it to Synapse media.

Canonicalises the name through egregore_tag_parser.normalize_egregore_name() so variant spellings collapse to one asset stem, then searches the filesystem under EGREGORE_ASSETS_DIR: curated images in the cradle_synthesis/ folder are preferred, falling back to the egregore’s own folder, trying several filename and extension variants. When a file is found it is read as bytes and POSTed to /_matrix/media/v3/upload using the pooled _get_session() with an image/png content type and the ghost’s user_id query, and the returned content_uri is handed back. Reads from the local filesystem and performs network I/O; missing files, non-200 responses, and exceptions are logged and yield None rather than raising.

Called by ensure_ghost(), whose result is fed straight into set_avatar(); no direct external callers were found.

Parameters:

name (str) – Human-facing egregore name whose avatar should be uploaded.

Returns:

The mxc:// content URI on success, or None if no avatar file exists or the upload fails.

Return type:

str | None

async ensure_ghost(name, display_name=None)[source]

Fully provision an egregore ghost: register, name, and avatar it.

The high-level summoning entry point: it calls register_ghost() (bailing out with False if registration fails), derives a display name from display_name or a title-cased form of name and applies it via set_display_name(), then attempts upload_avatar_from_disk() and, when that yields an MXC URI, set_avatar(). The avatar steps are best-effort – a missing avatar does not fail the call. Net effect is a ready-to-use ghost on Synapse with a profile and (usually) a picture.

Called by tools/summon_egregore.py through the module-level get_bridge() singleton when the bot summons an egregore.

Parameters:
  • name (str) – Human-facing egregore name to provision.

  • display_name (str | None) – Optional explicit display name; when omitted, a title-cased version of name (underscores to spaces) is used.

Returns:

True once the ghost is registered and named (regardless of whether an avatar was found), False if registration failed.

Return type:

bool

async join_room(name, room_id)[source]

Join an egregore ghost user into a Matrix room.

Resolves the ghost’s user ID via ghost_user_id() and POSTs to /_matrix/client/v3/join/{room_id} through _api(), impersonating the ghost so it appears as a member of the room and can subsequently speak there. Logs an info line on success and an error line on failure.

Called by tools/summon_egregore.py (via the get_bridge() singleton) so a freshly summoned egregore joins the active room.

Parameters:
  • name (str) – Human-facing egregore name identifying the ghost.

  • room_id (str) – The Matrix room ID the ghost should join.

Returns:

True when Synapse returns 200, False otherwise.

Return type:

bool

async leave_room(name, room_id)[source]

Remove an egregore ghost user from a Matrix room.

Resolves the ghost’s user ID via ghost_user_id() and POSTs to /_matrix/client/v3/rooms/{room_id}/leave through _api(), impersonating the ghost so it stops being a room member. Logs an info line on success; a failed leave is only logged at warning level, since a ghost that is already absent is not a hard error.

Called by tools/dismiss_egregore.py (via the get_bridge() singleton) when an egregore is banished from the room.

Parameters:
  • name (str) – Human-facing egregore name identifying the ghost.

  • room_id (str) – The Matrix room ID the ghost should leave.

Returns:

True when Synapse returns 200, False otherwise.

Return type:

bool

async send_message(name, room_id, text, html=None)[source]

Send a text (optionally HTML-formatted) message as a ghost user.

Resolves the ghost via ghost_user_id(), bumps the module-level _txn_counter to mint a unique transaction ID (avoiding txn_id collisions in tight summoning loops), and builds an m.room.message content of type m.text. When html is supplied it adds the org.matrix.custom.html format and formatted_body fields. The content is PUT to /_matrix/client/v3/rooms/{room_id}/send/m.room.message/{txn_id} through _api(), impersonating the ghost so the message is attributed to the egregore. Logs the resulting event ID on success or an error on failure. Mutates the global _txn_counter as a side effect.

Invoked dynamically by the multi-voice interleaving path in message_processor/generate_and_send.py, which dispatches each parsed egregore segment to its own ghost through the get_bridge() singleton.

Parameters:
  • name (str) – Human-facing egregore name identifying the speaking ghost.

  • room_id (str) – The Matrix room ID to post into.

  • text (str) – Plain-text body of the message.

  • html (str | None) – Optional HTML rendering published as formatted_body.

Returns:

The Matrix event_id on success, or None on failure.

Return type:

str | None

async send_emote(name, room_id, text)[source]

Send an emote (an italic /me action line) as a ghost user.

Resolves the ghost via ghost_user_id(), bumps the module-level _txn_counter for a collision-free transaction ID, and PUTs an m.room.message of msgtype m.emote to /_matrix/client/v3/rooms/{room_id}/send/m.room.message/{txn_id} through _api(), impersonating the ghost. This is the action-text sibling of send_message() and mutates the global _txn_counter as a side effect. Unlike send_message() it logs nothing.

No callers were found in the repo; this is a helper available to egregore dispatch code that wants to emit /me-style actions.

Parameters:
  • name (str) – Human-facing egregore name identifying the acting ghost.

  • room_id (str) – The Matrix room ID to post the emote into.

  • text (str) – The emote/action text.

Returns:

The Matrix event_id on success, or None on failure.

Return type:

str | None

egregore_bridge.get_bridge()[source]

Return the process-wide EgregoreBridge singleton, creating it once.

Lazily constructs an EgregoreBridge on first call – using the module-level Synapse config defaults – and caches it in the module global _bridge so every caller shares one bridge, one aiohttp session pool, and one _registered ghost cache.

Called by the egregore tooling – tools/summon_egregore.py, tools/compose_scene.py, tools/dismiss_egregore.py – and by message_processor/generate_and_send.py to reach the bridge rather than instantiating EgregoreBridge directly.

Returns:

The shared bridge instance.

Return type:

EgregoreBridge