Skip to content

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.

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

The lifecycle handler initializes Lingchu runtime services when the NoneBot driver starts and shuts them down when the driver stops.

  • on_startup calls start.startup.startup() to load configuration, menus, permissions, and the scheduler.
  • on_shutdown calls shutdown_scheduler_service() followed by shutdown_message_store().

The bot connection handler records lifecycle events and delivers pending restart feedback.

  • on_bot_connect records bot_connected and triggers send_pending_restart_feedback(bot).
  • on_bot_disconnect records bot_disconnected.

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_preprocessor normalizes the event and fire-and-forget calls services.message_store.handle_event_received.
  • run_postprocessor reads the identity from state and fire-and-forget calls services.message_store.handle_matcher_result.
  • event_postprocessor and run_preprocessor are reserved no-op placeholders.

The API audit handler records platform API calls after they complete.

  • on_called_api resolves the platform context and fire-and-forget calls services.message_store.handle_api_called when message storage and API call recording are enabled.
  • on_calling_api is a reserved no-op placeholder.

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.

To add a new runtime hook handler:

  1. Pick the NoneBot2 native decorator that matches the lifecycle you want to hook into. Each handler module registers its decorators at import time, so the only entrypoint the plugin needs is from . import hooks.

    Capability NoneBot2 decorator Reference module
    Driver startup / shutdown @driver.on_startup, @driver.on_shutdown hooks/handlers/lifecycle.py
    Bot connect / disconnect @driver.on_bot_connect, @driver.on_bot_disconnect hooks/handlers/bot_connection.py
    Event / matcher processing @event_preprocessor, @event_postprocessor, @run_preprocessor, @run_postprocessor hooks/handlers/message_store.py
    Bot API call @Bot.on_calling_api, @Bot.on_called_api hooks/handlers/api_audit.py
  2. Implement the handler with the NoneBot2 decorator signature. Use resolve_platform_context(bot) from hooks/adapters.py when you need an adapter-neutral identity, and normalize_message_event(bot, event) when you need a stable message envelope:

    from nonebot import get_driver
    from nonebot.adapters import Bot
    from ..adapters import resolve_platform_context
    driver = get_driver()
    @driver.on_bot_connect
    async def on_bot_connect(bot: Bot) -> None:
    ctx = resolve_platform_context(bot)
    if ctx is None:
    return
    ...
  3. Create a module under hooks/handlers/, for example hooks/handlers/my_feature.py. Register the NoneBot2 decorators in that module and keep business calls in services/ or repositories/.

  4. Export the module from hooks/handlers/__init__.py so from . import hooks loads it:

    from . import my_feature as my_feature