Platform Registry
Platform Registry
Section titled “Platform Registry”Lingchu Bot reasons about platform capabilities instead of concrete NoneBot adapters. The registry in platforms/registry.py maps abstract platform identities to NoneBot adapter identifiers, validates the user’s adapter selection, and exposes protocol implementations so feature code can degrade gracefully when an implementation lacks a capability.
Three-layer model
Section titled “Three-layer model”The registry is organized as three nested layers.
| Layer | Concept | Examples |
|---|---|---|
| Platform | A product surface with a stable identity and capability set | qq |
| Protocol | A specific protocol family exposed by an adapter | default, napcat |
| Implementation | A module path that realizes a protocol under an adapter | handle.qq.adapters.onebot11.default, handle.qq.adapters.onebot11.napcat |
- A platform profile (
PlatformProfile) declaresplatform_id,display_name, the set of adapter names it accepts, the NoneBot adapter identifiers it maps to, a frozen set ofPlatformCapabilityvalues, and an optionalpermission_modulepath. - A protocol implementation (
ProtocolImplementationInfo) declaresprotocol_id, theadapter_idit belongs to, adisplay_name, and themodule_paththat implements it.
Business code resolves a concrete adapter to its PlatformProfile through get_platform_profile(adapter_name) and never hard-codes adapter IDs.
Current profiles
Section titled “Current profiles”Only one platform profile is implemented today.
| Platform | Display name | NoneBot adapters | Implemented |
|---|---|---|---|
qq |
~onebot.v11 |
Yes |
The QQ profile declares the full capability set:
group_managementmember_moderationmember_profilegroup_profileannouncementapplication_operationmessage_storeapi_audit
Other platform profiles (such as Milky, native QQ, or OneBot V12) have been removed from the project. Configuring any of those adapter IDs in LINGCHUAdapter fails startup with PlatformAdapterUnknownError.
Adapter selection
Section titled “Adapter selection”LINGCHUAdapter (read from NoneBot global config) selects the adapter for each platform. When unset, the platform’s first nonebot_adapters entry is used, so the default is ~onebot.v11.
parse_configured_adapters()accepts a string ("~onebot.v11"), a+-delimited string ("~onebot.v11+~milky"), or a list/tuple, and normalizes each entry to a lowercase~-prefixed id.resolve_enabled_adapters()returns the single enabled adapter for each implemented platform. If a platform has more than one configured adapter, it raisesPlatformAdapterConflictError.is_profile_enabled(profile)anditer_enabled_profiles()report which platform profiles have an enabled adapter. The permission layer usesiter_enabled_profiles()so platform permission modules are only imported and seeded for platforms you actually use.validate_enabled_adapters_loaded()checks that every Lingchu-enabled adapter was actually registered by NoneBot; otherwise it raisesPlatformAdapterNotLoadedError.
is_adapter_enabled(adapter_name) is the runtime check used by message storage and API audit. Adapters that NoneBot registered but Lingchu did not select are ignored — they are treated as disabled, excluded from platform resolution, and grouped under the stable unknown platform in stored records.
Error handling
Section titled “Error handling”The registry raises specific exceptions for invalid configuration. All three live in platforms/registry.py.
| Exception | When raised |
|---|---|
PlatformAdapterUnknownError |
LINGCHUAdapter declares an adapter Lingchu has not implemented or cannot recognize |
PlatformAdapterConflictError |
Multiple known adapters are configured for the same platform |
PlatformAdapterNotLoadedError |
Lingchu selected an adapter that NoneBot did not register |
Each exception message includes the offending adapter ids, the adapters available for the platform, and a concrete suggestion (for example, set LINGCHUAdapter=~onebot.v11 in .env.dev).
Protocol implementations
Section titled “Protocol implementations”OneBot V11 has two protocol implementations, registered in _PROTOCOL_IMPLEMENTATIONS.
| Protocol id | Adapter id | Display name | Module path |
|---|---|---|---|
default |
~onebot.v11 |
Default | handle.qq.adapters.onebot11.default |
napcat |
~onebot.v11 |
NapCat | handle.qq.adapters.onebot11.napcat |
Both implementations live under the same NoneBot adapter (~onebot.v11). The runtime distinguishes them by querying the connected implementation’s get_version_info() and inspecting app_name and app_version. Feature code dispatches to the right implementation module based on that probe.
get_protocol_implementations(adapter_id=None) returns registered implementations, optionally filtered by adapter, and export_registry_for_seeding() exports structured metadata (platforms, adapters, protocol implementations) for database seeding through repositories/registry.py.
Capability-driven feature visibility
Section titled “Capability-driven feature visibility”Feature code shows or hides commands based on the implementation’s actual capabilities rather than assuming every OneBot V11 implementation supports every operation.
Group announcement
Section titled “Group announcement”The announcement handler in handle/qq/adapters/onebot11/default/announcement.py resolves the action at call time through _resolve_announcement_action(bot):
- Call
bot.get_version_info(). - Require
protocol_version == "v11". - Parse
app_versionwithpackaging.version.parse. - Match
app_name:NapCat.Onebotwithapp_version >= 4.18.0routes tosend_group_notice_napcatinhandle/qq/adapters/onebot11/napcat/announcement.py.- Any other
app_nameor an older version returns the localized error “不支持的 OneBot 版本” and the command finishes without calling the API.
The NapCat implementation calls the private _send_group_notice API through bot.call_api().
Group avatar and other features
Section titled “Group avatar and other features”Features that are not supported by a connected implementation are hidden or rejected the same way: the handler probes the implementation, and when the required capability is unavailable the command finishes with a localized “unsupported version” message rather than issuing an API call that would fail. This keeps the menu and command surface honest about what the current connection can actually do.
Permission module discovery
Section titled “Permission module discovery”Platform permission modules are discovered through the PlatformProfile.permission_module field rather than a hard-coded module path. The QQ profile declares permission_module = "..platforms.qq.permissions", which permissions/bootstrap.py imports dynamically. Adding a new platform profile with its own permission module does not require touching the permission bootstrap — it only needs the new profile to set permission_module.