Source code for iamai.context

"""Per-handler execution context exposed to plugin callbacks."""

from __future__ import annotations

from dataclasses import dataclass, field
from collections.abc import Callable
from typing import TYPE_CHECKING, Any

from .event import Event
from .message import Message


[docs] @dataclass(slots=True) class Context: """Runtime context passed to handlers and middleware.""" runtime: "Runtime" adapter: "Adapter" plugin: "Plugin" event: Event handler: "BoundHandler" matches: dict[str, Any] = field(default_factory=dict) @property def text(self) -> str: """Return the plain text carried by the current event.""" return self.event.text @property def args(self) -> str: """Return command arguments captured during command matching.""" return str(self.matches.get("args", "")) @property def command_name(self) -> str | None: """Return the matched command name, if any.""" value = self.matches.get("command") return None if value is None else str(value) @property def config(self) -> dict[str, Any]: """Return this plugin's validated configuration mapping.""" return self.plugin.config @property def state(self) -> dict[str, Any]: """Return this plugin's private state mapping.""" return self.plugin.state @property def shared_state(self) -> dict[str, Any]: """Return the runtime-wide shared state mapping.""" return self.runtime.state
[docs] async def reply(self, message: str | Message) -> Any: """Send a reply to the event's default target.""" return await self.adapter.send_message(Message.ensure(message), event=self.event)
[docs] async def send(self, message: str | Message, *, target: Any | None = None) -> Any: """Send a message to an explicit adapter target.""" return await self.adapter.send_message(Message.ensure(message), target=target)
[docs] async def call_api(self, action: str, **params: Any) -> Any: """Call an API action on the current adapter.""" return await self.adapter.call_api(action, **params)
[docs] async def reload_plugins(self) -> None: """Schedule a plugin reload after the current handler completes.""" self.runtime.request_plugin_reload()
[docs] async def wait_for_message( self, *, timeout: float | None = 60.0, rule: Callable[["Context"], Any] | None = None, ) -> "Context": """Wait for the next message in the same session.""" if rule is None: prefixes = self.runtime.command_prefixes() def is_plain_message(ctx: "Context") -> bool: return not ctx.text.strip().startswith(prefixes) rule = is_plain_message return await self.runtime.sessions.wait_for(self, timeout=timeout, rule=rule)
if TYPE_CHECKING: from .adapter import Adapter from .runtime import Runtime from .plugin import BoundHandler, Plugin