Skip to content

Adapter Guide

This guide explains how to configure and use adapters in onebots.

Supported Adapters

onebots currently supports the following platform adapters:

PlatformStatusPackageDescription
QQ Official Bot✅ Implemented@onebots/adapter-qqSupports QQ channels, group chats, private chats
ICQQ✅ Implemented@onebots/adapter-icqqSupports QQ via unofficial protocol with more complete features
Kook✅ Implemented@onebots/adapter-kookSupports channels, private chats, server management
WeChat✅ Implemented@onebots/adapter-wechatSupports WeChat Official Accounts
WeChat ClawBot (iLink)✅ Implemented@onebots/adapter-wechat-clawbotWeChat iLink Bot HTTP (QR login, long polling)
Discord✅ Implemented@onebots/adapter-discordSupports Discord bots
Telegram✅ Implemented@onebots/adapter-telegramSupports private chats, groups, channels
Feishu✅ Implemented@onebots/adapter-feishuSupports private chats, group chats, rich text messages
DingTalk✅ Implemented@onebots/adapter-dingtalkSupports enterprise internal apps and custom bots
Slack✅ Implemented@onebots/adapter-slackSupports channel messages, private chats, app commands
WeCom✅ Implemented@onebots/adapter-wecomSupports app message push, contact sync
Microsoft Teams✅ Implemented@onebots/adapter-teamsSupports channel messages, private chats, adaptive cards
Line✅ Implemented@onebots/adapter-lineSupports Line bot messages and events
Email✅ Implemented@onebots/adapter-emailSupports SMTP sending and IMAP receiving
WhatsApp✅ Implemented@onebots/adapter-whatsappSupports WhatsApp Business API
Zulip✅ Implemented@onebots/adapter-zulipSupports Zulip streams and private messages
Matrix✅ Implemented@onebots/adapter-matrixClient-Server API, AppService, and manual ingress
Google Chat✅ Implemented@onebots/adapter-google-chatInteraction HTTPS, Workspace Events, and manual ingress
Facebook Messenger✅ Implemented@onebots/adapter-facebook-messengerMessenger Platform, Graph API, webhook, and manual ingress
Instagram Messaging✅ Implemented@onebots/adapter-instagramInstagram Login, Messaging, Graph API, webhook, and manual ingress
Mattermost✅ Implemented@onebots/adapter-mattermostREST API v4, reliable WebSocket, existing sockets, and manual ingress
Twitch✅ Implemented@onebots/adapter-twitchHelix, EventSub WebSocket/Webhook, existing Host/socket, and manual ingress
IRCv3✅ Implemented@onebots/adapter-ircv3Modern IRC, CAP/SASL, TCP/TLS, existing sockets, and manual ingress

Capability manifests

Every adapter exports and registers one runtime capability manifest. It describes actions, events, message segments, and transports, distinguishing native support, emulated projections, and unsupported features. Context-dependent entries also declare their required permissions, availability, and scenes.

Use adapter.describeCapabilities(accountId) for the complete manifest and adapter.getSupportedActions(accountId) for callable actions. OneBots verifies that every advertised action has a concrete adapter implementation, preventing capability metadata from drifting away from runtime behavior.

The versioned manifest is a closed runtime contract rather than a TypeScript-only convention. Adapter registration and construction validate all four categories, support levels, availability, scenes, permissions, message directions, transport modes, and unknown fields, then retain a deeply immutable copy. A third-party JavaScript plugin therefore cannot publish a malformed or subsequently mutated manifest to the management API; a registration failure participates in the plugin transaction rollback.

The management API and Web capability panel now call describeCapabilities(accountId) for each configured account. To avoid repeating large manifests, /api/adapters keeps the adapter default in capabilities and places only object-distinct account overrides in accountCapabilities. Selecting an account in the Web panel explicitly shows either account-specific manifest or uses adapter default. An adapter may vary its manifest using stable token, plan, or permission information, but should not present transient network failures as capability changes.

Explicit event subscriptions are also account capability boundaries. QQ/Discord intents, Telegram allowed_updates, and Zulip event_types are projected into the canonical events that the selected account can actually receive. A webhook, reverse WebSocket, or manual mode that only changes ingress transport does not invent an upstream event filter when OneBots cannot observe one.

In the Web console, both Extensions and Bots → Capability overview show package-versioned catalog snapshots for platforms that are not installed, loaded, or configured with an account yet. Users can compare actions, events, message segments, and transports in the same overview without creating an account or entering credentials. Once an adapter is loaded, the overview gives its authoritative registered manifest precedence. After creating an account, users can switch to that account to inspect overrides caused by its token, permissions, or event subscriptions. A third-party plugin without a runtime manifest is explicitly marked as unknown rather than being hidden behind a catalog snapshot. The overview labels each source as a catalog snapshot or runtime manifest with its plugin version. Summary counts include native and emulated capabilities, while explicitly unsupported entries remain visible; permission, scene, and context restrictions appear on each item. The repository runs pnpm catalog:capabilities:check to keep the published snapshot aligned with every built adapter manifest.

Capability evidence distinguishes verified, unknown, and unavailable. If catalog integrity validation fails, the CLI and Web UI keep the affected platforms visible for diagnosis but do not display or search an unverified snapshot; empty categories cannot be interpreted as proof that a platform lacks those capabilities. unknown is reserved for a loaded plugin that did not declare a manifest, separate from catalog evidence that is unavailable.

The CLI can export each selected adapter's registered default manifest without starting an account:

bash
onebots capabilities
onebots capabilities --json
onebots capabilities --register <adapter-name-or-package>

Without an adapter selection, the command displays the complete catalog snapshot shipped with the current OneBots version. --register can temporarily load the authoritative manifest from an installed adapter. It does not connect to a platform or load protocols. JSON includes package names, versions, real entry paths, status, category counts, and complete manifests for selection reviews and CI evidence. Plugin load failures remain in errors and return exit code 2; an adapter without a registered default manifest or with unavailable catalog evidence sets complete to false and returns exit code 1. Account permission and subscription overrides remain available after startup through /api/adapters and the Web capability panel.

Native platform actions

Capabilities outside the common protocol surface are called through adapter.callAction(accountId, action, params). Each adapter package also exports a closed action set, its inferred action union, and a low-level executor. For QQ these are QQ_PLATFORM_ACTIONS, QQPlatformAction, and executeQQPlatformAction(). The set's has() accepts a dynamic string and narrows its type, so integrations do not need to duplicate action names or erase the native client type.

Named actions must declare their complete field allowlist, types, required relationships, and HTTP locations; APIs that combine query parameters with a JSON body model both separately. Only explicitly low-level entries such as call_*_api may carry a platform-native object. Typos, stale fields, and invalid types therefore fail with structured errors before a network request is sent instead of being forwarded silently.

ts
import {
  QQ_PLATFORM_ACTIONS,
  executeQQPlatformAction,
  type QQClient,
} from '@onebots/adapter-qq'

async function callQQ(client: QQClient, action: string, params: Record<string, unknown>) {
  if (!QQ_PLATFORM_ACTIONS.has(action)) throw new Error(`Unknown QQ action: ${action}`)
  return executeQQPlatformAction(client, action, params)
}

The Web Extensions view can browse the complete install catalog. Account and protocol forms use the adapters, protocols, and schemas registered by the active runtime generation. A plugin's registered schema is the single source for runtime validation, form sections, sensitive fields, and dynamic lists; the application does not maintain a second field catalog.

Keep using choices for closed enumerations. When an array should provide common suggestions while accepting ecosystem extension values, use ui.widget: 'choice-list' and explicitly set allowCustomValues: true. In that mode choices drives suggestions without rejecting custom strings. The flag is valid only on an array choice-list; invalid combinations fail during plugin registration.

Adapter names, protocol name-version pairs, and their configuration schema keys are unique within a process. The same factory or schema object may register repeatedly so plugin loading remains idempotent. A different implementation or schema cannot claim an occupied identifier: the registry throws a ValidationError instead of silently changing the implementation, metadata, or validation contract. Unregistering an implementation also removes its schema.

Plugin entries are resolved from the startup working directory with support for exports.import conditions, module, and main, then loaded through native ESM dynamic imports. Module initialization is awaited, so plugins may use top-level await. An initialization rejection is preserved in startup and doctor diagnostics instead of being misreported as a missing module.

A plugin must declare onebots, and @onebots/core when it uses core APIs directly, as peer dependencies supplied from the same installation root that starts the gateway. Before executing plugin code, the loader compares the real resolved package paths. Loading fails with both locations when a dependency manager installed a second copy, or when a global CLI attempts to load a plugin bound to a project-local OneBots installation. Run the project-local onebots command or install the plugin alongside the global CLI. A factory therefore cannot register into a separate static Registry and surface later as a misleading “initialized but did not register” error.

After initialization, the loader verifies the installation plan's plugin contract. An adapter must register the factory and schema named by the plan; a protocol must register the matching factory and <name>.<version> schema. A package that merely exports code, skips registration, or registers the wrong identity fails during installation verification, doctor, and service preflight diagnostics.

Plugin import and contract verification run as one serialized registry transaction. Each transaction may modify only the factory, metadata, and schema promised by its CLI name. Registering another adapter or protocol, or using another package to claim an identity that existed before import, reports the specific conflict and restores every adapter, protocol, schema, and protocol-version metadata entry to the pre-import state. Initialization errors and missing promised entries receive the same full rollback. Repeated loading of the same package and entry remains idempotent; multiple versions of one protocol may still share protocol metadata while registering their own factories and schemas. A failed plugin therefore cannot leave a partial registration or cause a false name conflict in the next plugin. Restart the process after fixing the package so Node.js imports the module again.

Install adapters

Do not run npm install directly in the OneBots runtime directory. The management service resolves the selected adapter together with its protocols, framework extensions and required peers, installs them into a new immutable generation, and verifies the complete runtime before activation. Modifying the active directory bypasses those checks and the recovery boundary.

The simplest path is the Install dependencies page in the Web console. Select platforms, protocols and frameworks, review the complete plan, then confirm it. The interactive terminal uses the same management API:

bash
onebots ui --data-dir <workspace> --setup

Private registry authorization is scoped to one installation and is never written to config.yaml. Headless deployments can use onebots control plan/install/installation/activate; pass authorization only through standard input with install --auth-stdin. See Quick start and Runtime updates for the complete flow and recovery rules.

Configuration

onebots uses YAML format configuration files, supporting multiple protocols per account.

Configuration Structure

yaml
# Global configuration
port: 6727              # HTTP server port
log_level: info         # Log level
timeout: 30             # Login timeout (seconds)

# General configuration (protocol default configuration)
general:
  onebot.v11:           # OneBot V11 protocol general configuration
    use_http: true
    use_ws: true
    access_token: ''
    heartbeat_interval: 5000
  onebot.v12:           # OneBot V12 protocol general configuration
    use_http: true
    use_ws: true
    access_token: ''
    heartbeat_interval: 5000
  satori.v1:            # Satori protocol general configuration
    use_http: true
    use_ws: true
    token: ''
  milky.v1:             # Milky protocol general configuration
    use_http: true
    use_ws: true
    token: ''

# Account configuration
# Format: {platform}.{account_id}
wechat.my_wechat_mp:
  # Protocol configuration
  onebot.v11:
    use_http: true
    use_ws: true
  
  # WeChat platform configuration
  app_id: your_app_id
  app_secret: your_app_secret
  token: your_token

For complete configuration examples, see Configuration Guide.