How to Implement Per-Group Service Enabling and Disabling in HoshinoBot
HoshinoBot uses a Service class with persistent JSON configuration to control which groups can access specific features via set_enable() and set_disable() methods.
Per-group service enabling and disabling in HoshinoBot allows bot administrators to restrict functionality to specific QQ groups or disable unwanted features globally or locally. This granular control is built into the core Service class in hoshino/service.py, which manages command triggers, scheduled jobs, and access permissions across the ice9coffee/hoshinobot framework.
Understanding HoshinoBot's Service Architecture
The Service Class
In HoshinoBot, functionality is organized into services—logical units that encapsulate commands, message handlers, and scheduled tasks. When you create a service in a module, you instantiate the Service class:
from hoshino import Service, priv
sv = Service('weather', use_priv=priv.NORMAL, manage_priv=priv.ADMIN, visible=True)
The service name 'weather' becomes the unique identifier for configuration storage and management commands.
Configuration Persistence
Service configurations are persisted to JSON files stored in ~/.hoshino/service_config/. According to the source code in hoshino/service.py, the _load_service_config method (lines 30-41) initializes each service by reading its configuration file, while _save_service_config (lines 43-58) writes changes back to disk.
Each service maintains two critical sets:
enable_group: Groups explicitly allowed to use the servicedisable_group: Groups explicitly blocked from using the service
Enabling and Disabling Services for Specific Groups
Using Management Commands
HoshinoBot ships with a built-in management module located at hoshino/modules/botmanage/service_manage.py that provides user-facing commands. Group administrators can enable or disable services directly in chat:
# Enable the weather service for the current group
.enable weather
# Disable the weather service for the current group
.disable weather
Super-users can manage multiple groups simultaneously by specifying group IDs:
# Super-user enabling service for specific groups
.enable weather 12345 67890 112233
Behind the scenes, these commands parse the service name and group IDs, then invoke the corresponding Service methods.
Programmatic Control with set_enable and set_disable
For developers building custom modules, the Service class exposes direct methods to control group access. In hoshino/service.py, the set_enable method (lines 44-48) adds a group to the enabled list while removing it from disabled:
sv = Service.get_loaded_services()['weather']
sv.set_enable(123456) # Group ID as integer
Conversely, set_disable (lines 50-54) blocks a specific group:
sv.set_disable(123456)
Both methods automatically persist changes to the JSON configuration file via _save_service_config, ensuring settings survive bot restarts.
Checking Service Availability at Runtime
The check_enabled Method
Before executing any command or trigger, HoshinoBot verifies whether the service is active for the current group. The check_enabled method in hoshino/service.py (lines 158-159) implements the following logic:
- If the group is in
enable_group, the service is active - If the group is not explicitly enabled but the service defaults to enabled and the group is not in
disable_group, the service is active - Otherwise, the service is inactive
Every trigger decorator (@sv.on_command, @sv.on_message, etc.) internally calls Service._check_all(event), which invokes check_enabled to filter incoming events. This ensures disabled services consume no processing resources for unauthorized groups.
Practical Implementation Examples
Defining a Service with Group Control
# my_module.py
from hoshino import Service, priv
# Create a visible service manageable by group admins
sv = Service('my_feature',
use_priv=priv.NORMAL,
manage_priv=priv.ADMIN,
visible=True)
@sv.on_command('mycommand')
async def my_command(session):
await session.send('Command executed!')
Programmatically Managing Group Access
def toggle_service_for_group(service_name: str, group_id: int, enable: bool):
"""
Enable or disable a service for a specific group.
Args:
service_name: The service identifier used during Service creation
group_id: The QQ group ID (integer)
enable: True to enable, False to disable
"""
services = Service.get_loaded_services()
if service_name not in services:
raise ValueError(f"Service '{service_name}' not found")
sv = services[service_name]
if enable:
sv.set_enable(group_id)
print(f"Enabled {service_name} for group {group_id}")
else:
sv.set_disable(group_id)
print(f"Disabled {service_name} for group {group_id}")
# Usage examples
toggle_service_for_group('my_feature', 123456789, enable=True)
toggle_service_for_group('my_feature', 987654321, enable=False)
Checking Service Status Before Execution
async def safe_service_call(session, service_name: str):
"""
Check if service is enabled for current group before processing.
"""
gid = session.event.group_id
sv = Service.get_loaded_services().get(service_name)
if not sv:
await session.send("Service not found")
return False
if not sv.check_enabled(gid):
await session.send("This service is disabled for this group")
return False
return True
Summary
- Service Architecture: HoshinoBot organizes functionality into
Serviceobjects defined inhoshino/service.py, each maintaining independent configuration in~/.hoshino/service_config/. - Per-Group Control: Use
Service.set_enable(group_id)andService.set_disable(group_id)to manage access, which automatically persists to JSON configuration files. - Runtime Checking: The
check_enabled(group_id)method filters all incoming events, ensuring disabled services never execute for unauthorized groups. - User Commands: Built-in management commands in
hoshino/modules/botmanage/service_manage.pyprovide.enableand.disablechat interfaces for administrators.
Frequently Asked Questions
How do I check if a service is enabled for a specific group programmatically?
Use the check_enabled() method on the service instance, passing the group ID as an integer. This method returns True if the service is active for that group, considering both explicit enable lists and default global settings. For example: sv.check_enabled(123456).
Where does HoshinoBot store per-group service configuration?
Configuration files are stored in the ~/.hoshino/service_config/ directory as individual JSON files named after each service (e.g., weather.json). The Service class automatically loads these on initialization and writes updates via _save_service_config() whenever set_enable() or set_disable() is called.
Can super-users manage services for groups they are not in?
Yes. According to the implementation in hoshino/modules/botmanage/service_manage.py, super-users can specify multiple group IDs after the service name when using the .enable or .disable commands. Regular group administrators can only toggle services for their current group.
What happens if a service is disabled for a group—will commands still trigger?
No. The Service._check_all() method intercepts all incoming events before they reach command handlers. If check_enabled() returns False for the current group ID, the event is silently dropped, meaning disabled services consume no processing resources and produce no responses for unauthorized groups.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →