Runtime Hooks
Runtime Hooks
Lingchu Bot registers all NoneBot runtime hooks through a single hooks/ package. Importing this package resolves the platform adapter layer, exposes shared hook interfaces, and triggers registration of every handler module. Business logic is kept separate from NoneBot decorators so handlers can be tested independently.
Unified entrypoint
The core plugin loads hooks by importing from . import hooks in its __init__.py. You do not need to import individual handler modules directly.
Hook categories
Runtime hooks are grouped by capability. Each category lives in its own module under hooks/handlers/.
| Category | Module | NoneBot hooks |
|---|---|---|
| Lifecycle | hooks/handlers/lifecycle.py | driver.on_startup, driver.on_shutdown |
| Bot connection | hooks/handlers/bot_connection.py | driver.on_bot_connect, driver.on_bot_disconnect |
| Message store | hooks/handlers/message_store.py | event_preprocessor, event_postprocessor, run_preprocessor, run_postprocessor |
| API audit | hooks/handlers/api_audit.py | Bot.on_calling_api, Bot.on_called_api |
Lifecycle
The lifecycle handler initializes Lingchu runtime services when the NoneBot driver starts and shuts them down when the driver stops.
on_startupcallsstart.startup.startup()to load configuration, menus, permissions, and the scheduler.on_shutdowncallsshutdown_scheduler_service()followed byshutdown_message_store().
Bot connection
The bot connection handler records lifecycle events and delivers pending restart feedback.
on_bot_connectrecordsbot_connectedand triggerssend_pending_restart_feedback(bot).on_bot_disconnectrecordsbot_disconnected.
Message store
The message store handler records event receipts and matcher results. It uses hooks/adapters.normalize_message_event to convert adapter events into a stable NormalizedMessageEvent and stores the resulting MessageIdentity in state so downstream hooks can correlate records.
event_preprocessornormalizes the event and fire-and-forget callsservices.message_store.handle_event_received.run_postprocessorreads the identity fromstateand fire-and-forget callsservices.message_store.handle_matcher_result.event_postprocessorandrun_preprocessorare reserved no-op placeholders.
API audit
The API audit handler records platform API calls after they complete.
on_called_apiresolves the platform context and fire-and-forget callsservices.message_store.handle_api_calledwhen message storage and API call recording are enabled.on_calling_apiis a reserved no-op placeholder.
Platform adapter layer
hooks/adapters.py owns all platform-specific knowledge used by runtime hooks.
| Export | Purpose |
|---|---|
resolve_platform_context(bot) | Maps a Bot instance to PlatformContext(platform_id, adapter_id, bot_id, protocol_id). Returns None for unknown adapters. |
normalize_message_event(bot, event) | Returns a NormalizedMessageEvent containing identity, event type, message type, text summary, and raw payloads. |
MessageIdentity / NormalizedMessageEvent | Stable dataclasses consumed by services.message_store. |
All field extraction helpers, such as _conversation_id, _user_id, _message_id, and _plain_text, live in this module so handlers do not duplicate adapter-specific logic.
Handler registry
hooks/registry.py provides an in-memory registry for typed hook handlers.
Prop
Type
The registry is typed against HookContext, EventContext, and the HookHandler protocol defined in hooks/interfaces.py.
Adding a new hook handler
To add a new runtime hook handler:
-
Choose a context type from
hooks/interfaces.py:PlatformContextfor bot-level identity.HookContextfor generic handlers.EventContextfor event-driven handlers.
-
Implement the handler as an async callable matching
HookHandler[T_contra]:from hooks.interfaces import EventContext, HookHandler async def my_handler(context: EventContext) -> None: ... -
Create a module under
hooks/handlers/, for examplehooks/handlers/my_feature.py. Register NoneBot decorators in that module and keep business calls inservices/orrepositories/. -
Export the module from
hooks/handlers/__init__.pysofrom . import hooksloads it:from . import my_feature as my_feature -
Add a registry entry if the handler should be discoverable by capability:
from hooks.interfaces import HookType from hooks.registry import register_handler register_handler(HookType.MESSAGE_STORE, my_handler)
Avoid side-effect imports
Do not rely on importing services.message_store or start.startup to register hooks. All registration side effects belong in hooks/ and are triggered by the single from . import hooks entrypoint.
Last updated on