How HoshinoBot Group Management and Anti-Spam Features Work: A Deep Dive into the Service Architecture

HoshinoBot implements group management and anti-spam through a modular Service class that wraps NoneBot event handlers with automatic permission checks, per-group configuration, and block-list integration.

The ice9coffee/hoshinobot repository structures every group-management function—whether anti-spam, anti-recall, or keyword filtering—around a lightweight Service abstraction. This design ensures consistent privilege validation and allows administrators to toggle features per group without modifying core logic.

Core Architecture: The Service Class and Permission Layer

Service Registration and Event Binding

At the heart of HoshinoBot's group management system lies the Service class defined in hoshino/service.py. This class acts as a wrapper that registers callbacks with the underlying NoneBot event system while automatically handling group-level enable/disable states.

When you create a service instance, you define its visibility and default availability:

from hoshino import Service

sv = Service('anti-kfc', enable_on_default=False)

The Service class provides decorators such as @sv.on_message, @sv.on_keyword, and @sv.on_notice to bind handler functions. These decorators create a ServiceFunc object and register it with the global trigger registry. When a matching event arrives—whether a keyword in chat or a message recall notice—the service's internal _check_all method executes a series of validation checks before running your handler.

Privilege and Block-List Checks

Before any anti-spam handler executes, the service validates the context through the hoshino.priv module. This system enforces a privilege hierarchy (SUPERUSER, ADMIN, NORMAL) and maintains global block-lists for users and groups.

The _check_all method performs three critical validations:

  1. self.check_enabled(group_id) — Verifies the service is explicitly enabled for the current group
  2. not priv.check_block_group(group_id) — Confirms the group is not globally blacklisted
  3. priv.check_priv(event, self.use_priv) — Ensures the sender has sufficient privilege to trigger the service

If any check fails, the handler aborts silently. When handlers need to punish offenders, they call priv.set_block_user(user_id, duration) to mute users or priv.set_block_group(group_id) to blacklist entire groups.

How Anti-Spam Modules Are Structured

HoshinoBot organizes all group-management logic under hoshino/modules/groupmaster/. Each anti-spam rule lives in a separate Python file, creating a Service instance and attaching specialized handlers via decorators.

Keyword-Based Filtering

The anti_kfc.py module demonstrates the standard pattern for keyword-based anti-spam. It creates a service disabled by default, then registers a keyword trigger for "疯狂星期四" (Crazy Thursday) spam:

from hoshino import Service, priv, util

sv = Service('anti-kfc', enable_on_default=False)

@sv.on_keyword('kfc', '疯狂星期四')
async def anti_kfc_crazy_thursday(bot, ev):
    # Block user for 4 minutes (240 seconds)

    priv.set_block_user(ev.user_id, timedelta(seconds=240))
    await util.silence(ev, 4 * 60, skip_su=False)
    await bot.send(ev, f'{ms.at(ev.user_id)} 检测到关键词,已被禁言4分钟')
    try:
        await bot.delete_msg(self_id=ev.self_id, message_id=ev.message_id)
    except CQHttpError:
        pass

This handler demonstrates the complete enforcement chain: block-list registration, timed muting via util.silence, warning message delivery, and offending message deletion.

Notice Event Handling

For non-message events like message recalls, anti_msg_recall.py uses @sv.on_notice to monitor group_recall events. When a user withdraws a message, the bot captures the recall event and reposts the original content to the group, preventing information hiding.

Other modules follow this pattern with specialized triggers:

  • anti_holo.py — Blocks Hololive-related keywords with immediate 10-minute mutes
  • anti_abuse.py — Implements a word-list filter and provides a ban_word command for admins to add forbidden terms dynamically
  • anti_lex.py — Combines scheduled jobs (@sv.scheduled_job) with keyword triggers for periodic reminders

The Groupmaster Module Directory

All group-management modules reside in hoshino/modules/groupmaster/, ensuring a unified location for moderation logic:

Each module follows the Service pattern, guaranteeing consistent privilege handling and per-group toggling capabilities.

Execution Flow of an Anti-Spam Rule

When a group message triggers an anti-spam rule, HoshinoBot executes a strict validation pipeline before taking action:

  1. Service Creation — The module initializes a Service instance, often with enable_on_default=False to require explicit admin activation
  2. Trigger Registration — Decorators like @sv.on_keyword bind the handler to specific event types in the NoneBot router
  3. Event Reception — NoneBot routes the group message to the appropriate trigger registry (keyword, notice, or command)
  4. Permission Validation — Service._check_all validates group enablement status, global block-lists, and user privileges in sequence
  5. Handler Execution — Upon passing all checks, the handler executes its logic: calling priv.set_block_user to update the block-list, invoking util.silence to apply timed mutes, sending warning messages, and optionally deleting the offending content via bot.delete_msg

This architecture ensures that adding a new anti-spam rule requires only creating a new module with a Service instance and attaching the appropriate decorator—no changes to core routing or permission logic are necessary.

Practical Implementation Examples

Creating a Custom Keyword Anti-Spam Rule

To add a new spam filter, create a file under hoshino/modules/groupmaster/ following this template:


# my_anti_spam.py

from datetime import timedelta
from hoshino import Service, priv, util
from hoshino.typing import CQEvent, CQHttpError, MessageSegment as ms

sv = Service('anti-spam-custom', enable_on_default=False)

@sv.on_keyword('spamword', 'advertisement')
async def block_spam(bot, ev: CQEvent):
    # Apply 5-minute block and mute

    priv.set_block_user(ev.user_id, timedelta(minutes=5))
    await util.silence(ev, 5 * 60, skip_su=False)
    
    # Notify and clean up

    await bot.send(ev, f'{ms.at(ev.user_id)} 广告信息已屏蔽')
    try:
        await bot.delete_msg(self_id=ev.self_id, message_id=ev.message_id)
    except CQHttpError:
        pass

The Service wrapper automatically guards this handler with the _check_all logic, ensuring it only runs when enabled for the group and the user isn't already blocked.

Enabling Services Per Group

Administrators toggle modules using the service's set_enable method:

from hoshino import Service, priv

sv = Service('admin-commands', visible=False)

@sv.on_command('enable_filter', aliases=('开启过滤',))
async def enable_filter(session):
    from hoshino.modules.groupmaster.my_anti_spam import sv as filter_sv
    filter_sv.set_enable(session.ctx['group_id'])
    await session.send('已在本群启用自定义过滤规则')

Users with priv.ADMIN or higher can invoke this command to activate specific anti-spam rules for their groups.

Summary

  • HoshinoBot's group management relies on the Service class in hoshino/service.py to wrap NoneBot handlers with automatic permission and configuration checks
  • Anti-spam modules reside in hoshino/modules/groupmaster/ and use decorators like @sv.on_keyword and @sv.on_notice to register triggers
  • Permission enforcement occurs through hoshino.priv, providing set_block_user, check_priv, and privilege levels (SUPERUSER, ADMIN, NORMAL)
  • Enforcement actions typically combine util.silence for timed mutes, priv.set_block_user for block-list updates, and bot.delete_msg for content removal
  • Per-group toggling is handled via Service.set_enable() and Service.set_disable(), allowing fine-grained control without code changes

Frequently Asked Questions

How do I enable anti-spam features for specific groups only?

Use enable_on_default=False when creating the Service instance, then provide an admin command that calls sv.set_enable(group_id). Only groups where an administrator explicitly enables the service will process its anti-spam handlers, as the _check_all method validates enablement status before execution.

What privilege levels control group management features?

HoshinoBot implements three privilege tiers in hoshino.priv: SUPERUSER (bot owner), ADMIN (group administrators), and NORMAL (regular users). The Service class checks these via priv.check_priv(event, required_level), and anti-spam handlers typically require ADMIN or higher to modify block-lists or enable services.

Can I customize the mute duration for spam violations?

Yes. When calling priv.set_block_user(user_id, duration), pass a timedelta object specifying the exact blocking period. Additionally, use util.silence(event, seconds) to apply the mute duration—both anti_kfc.py (4 minutes) and anti_holo.py (10 minutes) demonstrate different duration configurations.

How does the anti-recall feature capture deleted messages?

The anti_msg_recall.py module registers a handler via @sv.on_notice listening for group_recall events. When NoneBot detects a message withdrawal, the handler receives the original message ID and content from the event object, allowing the bot to repost the content before it disappears from the group history.

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 →