English | 简体中文 | 繁體中文 | 日本語 | Русский

ErisPulse

Write once, deploy to QQ / Telegram / Kook / Yunhu / WeChat Official Account / OneBot12 / ... multiple platforms.

An event-driven multi-platform chatbot development framework.

Based on the OneBot12 standard interface, write once and deploy to multiple platforms; flexible plugin system, hot reload support, and a complete developer toolchain, suitable for scenarios ranging from simple chatbots to complex automation systems.


Core Features

Event-Driven Architecture

A unified event model based on the OneBot12 standard—no more writing if/elif statements for message types per platform, a single handler automatically adapts to all adapters

Cross-Platform Compatibility

The same business code runs on all platforms—write once to serve QQ / Telegram / Kook / Yunhu / WeChat Official Account and over 15 other platforms, no need for repeated development

Modular Design

A flexible plugin system supports hot-swapping at runtime—install/uninstall/enable/disable modules without restarting the process, assembling robot capabilities like building blocks

Hot Reload

Development cycle shortened from 10 seconds to 0.5 seconds—save and the changes take effect immediately, development and debugging experience close to that of interpreted scripting languages

AI Assistance

Natural language descriptions directly generate usable modules—don't know how to write an adapter? Tell the AI which platform you want to connect to, and it will help you write it

Concise and Elegant

Intuitive chainable API design—complex logic such as @user, reply, retry, batch sending is completed in a single line, code is as light and readable as feathers


How It Works

ErisPulse uses an adapter layer to abstract platform differences, allowing business code to focus solely on events:

graph LR
    subgraph Platforms[Platforms]
        QQ["QQ"]
        TG["Telegram"]
        Kook["Kook"]
        YH["Yunhu"]
        WX["WeChat Official Account"]
    end

    subgraph Adapters[Adapter Layer]
        A1["QQ Adapter"]
        A2["Telegram Adapter"]
        A3["Kook Adapter"]
        A4["Yunhu Adapter"]
        A5["WeChat Adapter"]
    end

    Event["Event Bus<br/>Middleware → Distribute command/message/notice/request/meta"]

    subgraph Modules[Business Modules]
        M1["Command Handler<br/>@command"]
        M2["Message Handler<br/>@message"]
        M3["Your Module"]
    end

    QQ --> A1
    TG --> A2
    Kook --> A3
    YH --> A4
    WX --> A5

    A1 -->|"OB12 Event"| Event
    A2 -->|"OB12 Event"| Event
    A3 -->|"OB12 Event"| Event
    A4 -->|"OB12 Event"| Event
    A5 -->|"OB12 Event"| Event

    Event -->|"Distribute"| M1
    Event -->|"Distribute"| M2
    Event -->|"Distribute"| M3

    M1 -.->|"event.reply()<br/>SendDSL"| Event
    Event -.->|"Send"| A1
  • Adapter Layer converts native protocols from each platform into OneBot12 standard events, so business modules are unaware of platform differences
  • Event Bus executes the middleware chain first, then distributes events to five types of processors based on event type
  • Your Code subscribes to events via decorators, and replies using event.reply() or SendDSL—replies follow the same path back to the platform

For detailed design of the complete module composition, initialization process, and lifecycle events, see Architecture Overview.


Quick Start

One-Click Installation Script (Recommended)

The installation script automatically detects your environment (Docker, Python, uv), guides you to choose the most suitable installation method, and supports multiple languages (Chinese/English/Japanese/Russian/Traditional Chinese).

Windows (PowerShell):

irm https://get.erisdev.com/install.ps1 -OutFile install.ps1; powershell -ExecutionPolicy Bypass -File install.ps1

macOS / Linux:

curl -fsSL https://get.erisdev.com/install.sh -o install.sh && chmod +x install.sh && ./install.sh

Docker Installation Demo

pip Installation Demo

Using Docker (Recommended)

docker pull erispulse/erispulse:latest

If Docker Hub is inaccessible, you can use GitHub Container Registry:

docker pull ghcr.io/erispulse/erispulse:latest

When using the ghcr.io image, you need to modify the docker-compose.yml image:

image: ghcr.io/erispulse/erispulse:latest
# Download docker-compose.yml
curl -O https://raw.githubusercontent.com/ErisPulse/ErisPulse/main/docker-compose.yml

# Set Dashboard login token and start
ERISPULSE_DASHBOARD_TOKEN=your-token docker compose up -d

After starting, access http://<host>:8000/Dashboard and log in using the set token to the Dashboard management panel.

The image includes the ErisPulse framework and Dashboard management panel, supporting linux/amd64 and linux/arm64 architectures.

Persistence: Configuration files and installed modules/adapters are persisted to the host machine via volume mounting, so they won't be lost after container restart. Framework updates are completed through Dashboard hot updates.

Variable Default Description
ERISPULSE_DASHBOARD_TOKEN empty Dashboard login token (automatically written to configuration after setting)
ERISPULSE_PORT 8000 Dashboard port mapping
ERISPULSE_TAG latest Image tag, can be set to dev for pre-release images
ERISPULSE_BUILD_TARGET production Build target: production (stable version) or dev (pre-release version)
CONTAINER_NAME erispulse Container name
TZ Asia/Shanghai Container timezone
LANG en_US.UTF-8 System language, automatically detects startup interface language
ERISPULSE_LANG empty Force startup interface language: zh / zh_TW / en / ja / ru (overrides LANG)

1Panel App Store

Install ErisPulse with one click through the 1Panel app store, see ErisPulse-1Panel.

bash <(curl -sL https://get-1panel.erisdev.com/install.sh)

ErisPulse is available in the 1Panel third-party app store, and can be installed using the okxlin/appstore third-party repository.

Using pip to Install

pip install ErisPulse

You can also use the one-click installation script above, which automatically detects the environment and guides configuration.

Initialize Project

# Interactive initialization
epsdk init

# Quick initialization (specify project name)
epsdk init -q -n my_bot

Create Your First Bot

Create a main.py file:

Command Handler

from ErisPulse import sdk
from ErisPulse.Core.Event import command

@command("hello", help="Send a greeting message")
async def hello_handler(event):
    user_name = event.get_user_nickname() or "friend"
    await event.reply(f"Hello, {user_name}!")

@command("ping", help="Test if the bot is online")
async def ping_handler(event):
    await event.reply("Pong! The bot is running normally.")

if __name__ == "__main__":
    import asyncio
    asyncio.run(sdk.run(keep_running=True))

Effect Description

Send /hello

Bot replies: Hello, {username}!


Send /ping

Bot replies: Pong! The bot is running normally.


Running Method

epsdk run main.py
# Or in development mode
epsdk run main.py --reload

For more detailed instructions, see:


The Same Code. Multiple Platforms.

Exactly the same command handler. Different platforms. No modification to business logic required.

Kook

QQ

Yunhu


Chainable Send DSL

A single chainable call completes all sending logic including @user, reply, retry, timeout, and callback:

yunhu = sdk.adapter.get("yunhu")

# Single send: @user + reply + retry + success callback
await (yunhu.Send.To("group", "123")
       .At("456").Reply("msg_789")
       .Retry(3).Timeout(10)
       .Hook(lambda r: print("Send successful!"))
       .Text("Hello"))

# Batch send: send multiple messages in one chain
results = await (yunhu.Send.To("user", "123")
                .Build()
                .Text("Notification 1")
                .Image("pic.jpg")
                .Retry(2)
                .send_all())

Supports Hook (success callback), Retry (failure retry), Timeout (timeout cancellation), OnProgress (progress monitoring), Defer (delayed sending), Build (batch construction) and other chainable methods, see SendDSL Documentation.


Multi-Turn Conversation Example

ErisPulse includes a powerful multi-turn conversation engine, making it easy to implement guided operations, information collection, and other interactive scenarios:

from ErisPulse.Core.Event import command, request

@command("register")
async def register_handler(event):
    conv = event.conversation(timeout=60)
    
    await conv.say("Welcome to register!")
    
    # Multi-step collection of user information, with automatic validation
    data = await conv.collect([
        {"key": "name", "prompt": "Please enter your name"},
        {"key": "age", "prompt": "Please enter your age",
         "validator": lambda e: e.get_text().strip().isdigit(),
         "retry_prompt": "Age must be a number, please re-enter"},
    ])
    
    if data and await conv.confirm(f"Confirm registration? Name: {data['name']}, Age: {data['age']}"):
        # Push notification using SendDSL
        await sdk.adapter.get(event.get_platform()).Send.To(
            "user", event.get_user_id()
        ).Text(f"Registration successful! Welcome {data['name']}")
        # Or await event.reply("Registration successful!")

# Automatically handle friend requests
@request.on_friend_request()
async def handle_friend_request(event):
    user_name = event.get_user_nickname() or event.get_user_id()
    
    # Approve the request
    result = await event.approve()
    if result.get("status") == "ok":
        await event.reply(f"Friend request automatically approved, welcome {user_name}")
@command("quiz")
async def quiz_handler(event):
    conv = event.conversation(timeout=30)
    
    # Multiple choice question
    answer = await conv.choose("Who is the creator of Python?", [
        "Guido van Rossum",
        "James Gosling", 
        "Dennis Ritchie",
    ])
    
    if answer == 0:
        await conv.say("Correct!")
    elif answer is None:
        await conv.say("Timed out, try again next time!")
    else:
        await conv.say("Incorrect, the correct answer is Guido van Rossum")

@command("menu")
async def menu_handler(event):
    conv = event.conversation(timeout=60)
    
    # Branching, build complex interaction flow
    @conv.branch("main")
    async def main_menu():
        await conv.say("=== Main Menu ===\n1. Personal Information\n2. Settings\n3. Exit")
        resp = await conv.wait()
        if resp and resp.get_text().strip() == "1":
            await conv.goto("profile")
    
    @conv.branch("profile")
    async def profile():
        await conv.say("Name: Alice\n0. Return")
        resp = await conv.wait()
        if resp and resp.get_text().strip() == "0":
            await conv.goto("main")
    
    await conv.start()

See Conversation Multi-Turn Dialogue


Core Modules

ErisPulse provides a complete multi-platform chatbot development toolchain, with each core module serving its own purpose:

graph TB
    SDK["sdk<br/>Unified Entry Point"]

    SDK --> Event["Event<br/>Event System"]
    SDK --> AdapterMgr["Adapter<br/>Adapter Management"]
    SDK --> ModuleMgr["Module<br/>Module Management"]
    SDK --> Router["Router<br/>HTTP/WS Routing"]
    SDK --> Storage["Storage<br/>SQLite Storage"]
    SDK --> Config["Config<br/>Configuration Management"]
    SDK --> Lifecycle["Lifecycle<br/>Lifecycle"]
    SDK --> Logger["Logger<br/>Logging System"]
    SDK --> Client["HttpClient<br/>HTTP Client"]
Module Description
Event Event system, providing five types of events: command / message / notice / request / meta + Conversation multi-turn dialogue
Adapter Adapter management, BaseAdapter base class for unified event conversion and SendDSL sending, supporting over 15 platforms including QQ / Telegram / Kook / Yunhu / WeChat Official Account
Module Module management, BaseModule base class + dependency declaration and topological sorting for loading
SendDSL Chainable sending, complex logic such as @/reply/retry/timeout/batch is completed in one line
Router HTTP/WebSocket routing system (FastAPI + Uvicorn)
Storage Key-value storage based on SQLite + generic SQL chainable query
Config TOML configuration management
Lifecycle Lifecycle event hooks (core.init / adapter.* / module.*)
Logger Modular logging system, supports sub-loggers
HttpClient Unified HTTP/WS client (based on aiohttp), built-in retry and ErisPulse exception system

For more design details (initialization process, lifecycle events, module loading strategy), see Architecture Overview.


Ecosystem

ErisPulse is not just a framework. You can start immediately after installation, without having to build wheels from scratch.

Framework

Core runtime

Unified event & message model

Dashboard

Visual management

Plugins · Logs · Configuration

Online Demo →

AI Builder

Natural language → usable module

Experience Now →

Module Market

Plug-and-play plugins

Explore Modules →

Adapters

Access to 15+ platforms

Documentation

erisdev.com

Docker

Multi-architecture support

erispulse/erispulse

CLI

epsdk scaffolding tool


Supported Platforms

We welcome contributions to adapters!

Adapter Description
Kook Instant messaging platform Kook (Open Black)
Matrix Decentralized communication protocol Matrix
OneBot11 General robot protocol OneBot v11
OneBot12 Standard OneBot v12 protocol
QQ Official QQ robot platform
Sandbox Web-based debugging, no need to connect to a real platform
Telegram Global instant messaging platform
Email Email protocol adapter for sending and receiving
Yunhu Enterprise-level instant messaging platform (robot access)
Yunhu User Access adapter based on Yunhu user protocol
Ideaura Café Allons! (・ω・) /
Discord Global community communication platform, supports servers, channels, and private messages
Webhook General HTTP bridge adapter, connects to any system
WeChat Official Account Official WeChat Official Account platform

See Adapter Details


Community

Connect with us:


Contribution Guidelines

The health of the ErisPulse project still needs your contribution! We welcome contributions in various forms:

  1. Report Issues — Submit bug reports in GitHub Issues
  2. Feature Requests — Propose new ideas through Community Discussions
  3. Code Contributions — Please read the Code Style and Contribution Guidelines before submitting PRs
  4. Documentation Improvements — Help improve documentation and example code

Join Community Discussions


Acknowledgments

Some code in this project is based on sdkFrame.

The core adapter standardization layer references and benefits from the OneBot12 Specification.

Special thanks to the Yunhu ecosystem and community.

The early exploration and growth of ErisPulse would not have been possible without the support of the Yunhu developer community, many ideas, adapters, and practical experiences originated here.

We also thank all developers and project authors who have contributed to ErisPulse, OneBot, and the open-source community.