# How to Create and Load Custom Modules into HoshinoBot: The Complete Guide

> Learn to create and load custom modules into HoshinoBot. Follow this guide to register handlers and configure your bot for new functionality.

- Repository: [ice9coffee/hoshinobot](https://github.com/ice9coffee/hoshinobot)
- Tags: how-to-guide
- Published: 2026-03-03

---

**To create and load custom modules into HoshinoBot, create a Python package under `hoshino/modules/`, instantiate a `Service` object to register your handlers, and add the module name to `MODULES_ON` in your bot configuration file.**

HoshinoBot is an open-source QQ bot framework developed by `ice9coffee/hoshinobot` that uses a modular architecture where each functional extension is treated as an independent plugin. Creating custom modules allows you to extend the bot's capabilities while maintaining clean separation between core functionality and third-party features. This guide covers the exact steps to build, configure, and load custom modules using the bot's internal APIs.

## Understanding HoshinoBot's Module Architecture

HoshinoBot automatically discovers and loads modules during the initialization phase. The system relies on a specific directory structure and a configuration-based whitelist to determine which packages to import.

### The MODULES_ON Configuration

During bot start-up, [`hoshino/__init__.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/__init__.py) reads the list `config.MODULES_ON` and automatically loads every folder name found there as a plugin. This configuration is defined in the user-editable file [`hoshino/config_example/__bot__.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/config_example/__bot__.py), where you declare which modules should be active by adding their folder names to the `MODULES_ON` set.

### The Service Class API

Each module must define one or more **Service** objects using the `Service` class located in [`hoshino/service.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/service.py). The `Service` class provides decorator methods such as `on_prefix`, `on_rex`, `on_command`, and `on_fullmatch` that register your callback functions with Hoshino's trigger system. As implemented in `ice9coffee/hoshinobot`, the `Service` constructor accepts parameters like `name`, `help_`, `enable_on_default`, and `visible` to control module behavior and visibility.

## Step-by-Step Guide to Creating a Custom Module

Follow these exact steps to create a functional custom module that HoshinoBot will recognize and load automatically.

1. **Create the package directory** under `hoshino/modules/your_module`. The folder name determines how you will reference the module in configuration.

2. **Create the main module file** (e.g., [`hello.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hello.py)) that instantiates a `Service` and defines handlers:

```python
from hoshino import Service, priv

# Define the service; name should match the folder name

sv = Service('hello', help_='Say hello to the bot', enable_on_default=True)

@sv.on_prefix('hi')
async def say_hello(bot, ev):
    """Triggered by messages starting with 'hi'."""
    await bot.send(ev, 'Hello! 👋')

@sv.on_fullmatch('hello')
async def hello_fullmatch(bot, ev):
    """Triggered by exact message 'hello'."""
    await bot.send(ev, 'Greetings!')

```

3. **Add an optional [`__init__.py`](https://github.com/ice9coffee/hoshinobot/blob/main/__init__.py)** in the module folder if you need to organize multiple files or import submodules.

4. **(Optional) Create module-specific configuration** by adding [`hoshino/config/hello.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/config/hello.py) with custom variables:

```python

# Custom configuration values for the hello module

GREETING_MESSAGE = 'Hello! 👋'
ENABLE_EMOJI = True

```

5. **Enable the module** by editing [`hoshino/config_example/__bot__.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/config_example/__bot__.py) (or your production copy at [`hoshino/config/__bot__.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/config/__bot__.py)) and adding the module name to the set:

```python
MODULES_ON = {
    'botmanage',
    'dice',
    'groupmaster',
    'hello',  # ← Add your module name here

    # other modules...

}

```

6. **Restart the bot**. When `hoshino.init()` executes, it scans `MODULES_ON` and imports your module automatically from `hoshino/modules/hello/`.

## Module Configuration and Advanced Options

HoshinoBot supports sophisticated configuration patterns beyond basic command handlers. You can reference built-in modules like `dice` (located at [`hoshino/modules/dice/dice.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/modules/dice/dice.py)) to see production-ready patterns for complex triggers and permission management.

### Directory Structure Reference

A complete custom module follows this layout:

```

hoshino/
├─ modules/
│   └─ hello/
│       ├─ __init__.py      # Optional package initializer

│       └─ hello.py         # Main logic with Service definitions

├─ config/
│   └─ hello.py             # Optional module-specific config

└─ config_example/
    └─ __bot__.py           # Add 'hello' to MODULES_ON here

```

### Trigger Types Available

The `Service` class in [`hoshino/service.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/service.py) exposes several decorators for different message matching patterns:

- **`on_prefix`** – Matches messages starting with the specified string
- **`on_fullmatch`** – Matches exact message content
- **`on_rex`** – Matches regular expression patterns
- **`on_command`** – Matches commands with specific syntax and argument parsing

## Summary

To successfully create and load custom modules into HoshinoBot:

- Place module code under `hoshino/modules/{module_name}/` as a valid Python package
- Instantiate a `Service` object from [`hoshino/service.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/service.py) to register handlers using decorators like `@sv.on_prefix()`
- Add the module name to the `MODULES_ON` set in your bot configuration file
- Optionally create `hoshino/config/{module_name}.py` for module-specific settings
- Restart the bot to trigger the automatic loading mechanism in [`hoshino/__init__.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/__init__.py)

## Frequently Asked Questions

### What is the minimum required code for a functional HoshinoBot module?

At minimum, a module needs a Python file under `hoshino/modules/{name}/` that creates a `Service` instance and registers at least one handler using a decorator like `@sv.on_prefix()` or `@sv.on_fullmatch()`. The folder name must match the service name, and the module must be listed in `MODULES_ON`.

### Can I create modules without editing the config_example files?

Yes. Copy [`hoshino/config_example/__bot__.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/config_example/__bot__.py) to [`hoshino/config/__bot__.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/config/__bot__.py) and edit the copy instead. HoshinoBot prioritizes the configuration in the `config/` directory over the example files, allowing you to keep custom modules separate from the repository examples.

### How do I debug why my custom module isn't loading?

Check three specific locations: ensure the folder name under `hoshino/modules/` exactly matches the string added to `MODULES_ON`, verify there are no Python syntax errors in your module files, and check that the `Service` object is properly instantiated before decorators are applied. The bot imports modules during `hoshino.init()`, so any import errors will appear in the console during startup.

### Are there permission controls available for custom modules?

Yes. The `Service` class accepts a `priv` parameter (imported from `hoshino import priv`) to restrict commands to specific privilege levels such as `priv.ADMIN` or `priv.SUPERUSER`. You can also use the `@sv.only_to_me()` decorator or check `await priv.check_priv(ev, priv.ADMIN)` within your handler functions.