How to Add Support for New Games to HoshinoBot: A Complete Kancolle Integration Guide

To add support for new games like Kancolle (艦隊これくしょん) to HoshinoBot, create a Python package under hoshino/modules/, instantiate the Service class from hoshino/service.py, and register command handlers using decorators like @sv.on_fullmatch or @sv.scheduled_job.

HoshinoBot operates on a service-oriented architecture where each game or feature exists as a modular service. By following the existing Kancolle implementation in hoshino/modules/kancolle/, you can integrate new games with command handling, scheduled reminders, and external API queries while leveraging the bot's built-in permission system.

Understanding the Service Architecture

The core of HoshinoBot's extensibility lies in the Service class defined in hoshino/service.py. Each game module creates a Service instance that registers the module with the bot's core, provides permission handling via _check_all, and creates a dedicated logger. When the bot starts, it iterates over the MODULES list in the configuration and imports each module, automatically executing the service registration code at the top level.

Create the Module Package

Begin by creating a new directory under hoshino/modules/ for your game. For Kancolle, this is hoshino/modules/kancolle/. Add an empty __init__.py file to make it a proper Python package. If your game requires sub-modules for data queries, create subdirectories like query/ with their own __init__.py files. This isolation keeps game-specific code organized and ensures Python can resolve imports correctly.

Instantiate the Service Class

In your main module file (e.g., reminder.py), import and instantiate the Service class with a unique name, bundle identifier, and help text:

from hoshino import Service

sv = Service('kc-reminder', bundle='kancolle', help_='Kancolle 定时提醒功能')

The bundle parameter groups related services together, while the service name must be unique across the entire bot instance. This instantiation registers your module with HoshinoBot's internal service registry and initializes the permission checking system.

Register Command Handlers

Use the decorators provided by your Service instance to bind chat commands to coroutines. The @sv.on_fullmatch decorator triggers on exact text matches, while @sv.on_prefix and @sv.on_message offer pattern-based matching:

@sv.on_fullmatch('演习提醒')
async def enshu_reminder(bot, ev):
    await bot.send(ev, '演習即将刷新!记得出击哦~')

These decorators automatically hook your coroutine into the bot's event loop and apply group-level permission checks before execution. The bot parameter provides the API interface, while ev contains the event context including user_id and group information.

Schedule Periodic Jobs

For games requiring timed reminders or daily resets, use the @sv.scheduled_job decorator, which integrates with APScheduler. The following example from hoshino/modules/kancolle/reminder.py sends a daily notification at 03:30 Shanghai time:

@sv.scheduled_job('cron', hour='3', minute='30')
async def daily_quest_refresh_reminder():
    await sv.broadcast('提督,今天的任务已经刷新!快去完成吧~')

Scheduled jobs respect the service's enabled status, meaning they only broadcast to groups that have explicitly enabled your game service. The jobs are executed by the built-in APScheduler and run in the Asia/Shanghai timezone.

Query External Game APIs

When your game requires live data (fleet composition, expedition status, or player rankings), create a query/ sub-package with async functions. Following the Kancolle pattern in hoshino/modules/kancolle/query/fleet.py:

import aiohttp

API_URL = 'https://api.kcwiki.moe/kancolle/fleet/'

async def get_fleet_info(member_id: int) -> dict:
    async with aiohttp.ClientSession() as sess:
        async with sess.get(f'{API_URL}{member_id}') as resp:
            return await resp.json()

Keep network logic separate from command handlers to maintain clean separation between "bot glue" and "game API" concerns. Import these query functions into your main service file to fetch data before formatting responses.

Persist Game State with SQLite

For storing rankings, cooldowns, or user progress, use a SQLite database under ~/.hoshino/. Reference the GameMaster pattern from hoshino/modules/priconne/games/desc_guess.py:

import os
from hoshino.modules.priconne.games.game_master import GameMaster

DB_PATH = os.path.expanduser("~/.hoshino/kancolle_data.db")
gm = GameMaster(DB_PATH)

The GameMaster class abstracts SQLite operations, or you can implement your own thin wrapper using standard sqlite3 or aiosqlite for async operations.

Register the Module in Configuration

Finally, add your module to the bot's configuration. List the module path in the MODULES array within your config file (typically hoshino/config/__bot__.py). For example, append 'hoshino.modules.kancolle' to the list. HoshinoBot automatically imports and initializes all listed modules on startup, executing the Service construction code immediately upon import.

How Message Processing Works

When a message arrives, NoneBot dispatches it to HoshinoBot's on_message hook. The framework then calls Service._check_all to verify permissions and group enable status before invoking your decorated coroutine. For scheduled jobs, Service.scheduled_job registers the coroutine with APScheduler; when triggered, the wrapper logs execution, catches exceptions, and uses Service.broadcast to push messages to all enabled groups.

Summary

  • Create a package under hoshino/modules/<game>/ with proper __init__.py files to isolate your game code
  • Instantiate Service with a unique name and bundle identifier to register with the bot core and enable permission handling
  • Use decorators like @sv.on_fullmatch for commands and @sv.scheduled_job for periodic reminders
  • Separate API logic into a query/ sub-package using async functions to keep network code organized
  • Persist data via SQLite in ~/.hoshino/ following existing GameMaster patterns for state management
  • Enable the module by adding the full module path to the MODULES configuration list before starting the bot

Frequently Asked Questions

How do I enable my new game module after creating the files?

Add the module path to the MODULES list in your HoshinoBot configuration file, typically located at hoshino/config/__bot__.py. Append the Python import path (e.g., 'hoshino.modules.kancolle') to the array. The bot automatically imports and initializes the service when it starts; no additional registration code is required beyond creating the Service instance in your module.

What is the difference between @sv.on_fullmatch and @sv.on_prefix?

@sv.on_fullmatch('command') triggers only when the message content exactly matches the specified string, while @sv.on_prefix('command') triggers when the message starts with the specified string, allowing you to capture arguments after the command. Both decorators automatically apply the service's permission checks through the internal _check_all method defined in hoshino/service.py before executing your handler.

Can I use databases other than SQLite for game state persistence?

Yes, though SQLite is the convention used in existing modules like hoshino/modules/priconne/games/desc_guess.py. You can implement any async-compatible database by initializing the connection in your module file and using it within your command handlers. Ensure database files are stored under ~/.hoshino/ to maintain consistency with the project's directory structure.

How do I restrict commands to specific groups or users?

The Service class automatically handles group-level enable/disable functionality through its configuration system. Groups must explicitly enable your service using the bot's master control commands before receiving any messages or triggering handlers. For finer control, inspect ev.group_id or ev.user_id within your handler coroutine, or implement custom permission logic using the bot's superuser configuration.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →