platforms.webchat module

WebChat platform adapter – browser-based real-time chat via WebSocket.

Plugs into BotRunner the same way Discord and Matrix do. Incoming WebSocket messages become IncomingMessage instances; outbound replies are pushed back as JSON frames.

# πŸ”₯πŸ’€ STAR GETS A WEB BODY. THE LATTICE EXPANDS.

class platforms.webchat.ConnectionManager[source]

Bases: object

Track active WebSocket sessions per user.

Thread-safe via asyncio (single event loop).

async connect(ws, user_id, user_name, avatar='')[source]

Register a new WebSocket connection.

Return type:

_WebSocketSession

Parameters:
async disconnect(session)[source]

Remove a WebSocket connection.

Return type:

None

Parameters:

session (_WebSocketSession)

get_sessions(user_id)[source]

Return all active sessions for a user.

Return type:

list[_WebSocketSession]

Parameters:

user_id (str)

property active_users: list[str]

Return all connected user IDs.

property total_connections: int

Total number of active WebSocket connections.

class platforms.webchat.WebChatPlatform(message_handler, **kwargs)[source]

Bases: PlatformAdapter

Browser-based chat platform using WebSocket for real-time comms.

Unlike Discord/Matrix, this adapter doesn’t connect to an external service. Instead it exposes a ConnectionManager that the FastAPI WebSocket endpoint populates. Outgoing messages are pushed to all active sessions for the target user.

Parameters:
  • message_handler (MessageHandler)

  • kwargs (Any)

property name: str

Short lowercase identifier, e.g. "matrix" or "discord".

property is_running: bool

Return True while the platform event loop is active.

property bot_identity: dict[str, str]

Return the bot’s own identity on this platform.

Returns a dict with at minimum platform and user_id. Adapters should override to provide display_name and mention where available. The default returns an empty user_id (safe to call before login completes).

async start()[source]

No-op – lifecycle managed by FastAPI.

Return type:

None

async stop()[source]

No-op – lifecycle managed by FastAPI.

Return type:

None

async send(channel_id, text)[source]

Send a plain-text message to the user’s browser.

Return type:

str

Parameters:
async send_file(channel_id, data, filename, mimetype='application/octet-stream')[source]

Send a file/media attachment as base64 JSON.

Return type:

str | None

Parameters:
async send_with_buttons(channel_id, text, view=None)[source]

Send a message with interactive buttons for S.N.E.S. choices.

The view parameter is expected to be a list of dicts: [{"label": "Attack", "emoji": "swords", "custom_id": "..."}]

For Discord views (discord.ui.View), we extract button info. For raw dicts, we pass them through directly.

Return type:

str

Parameters:
async edit_message(channel_id, message_id, new_text)[source]

Edit an existing message in the browser.

Return type:

bool

Parameters:
  • channel_id (str)

  • message_id (str)

  • new_text (str)

async start_typing(channel_id)[source]

Show typing indicator in the browser.

Return type:

None

Parameters:

channel_id (str)

async stop_typing(channel_id)[source]

Hide typing indicator in the browser.

Return type:

None

Parameters:

channel_id (str)

create_sse_queue(request_id)[source]

Create a response queue for a SillyTavern SSE request.

Return type:

Queue

Parameters:

request_id (str)

remove_sse_queue(request_id)[source]

Clean up an SSE queue after the request completes.

Return type:

None

Parameters:

request_id (str)

async fetch_history(channel_id, limit=100)[source]

WebChat doesn’t maintain its own history – Redis has it.

Return type:

list[HistoricalMessage]

Parameters:
  • channel_id (str)

  • limit (int)