How to Manage and Implement Clan Battle (公会战) Features in HoshinoBot
To manage clan battles in HoshinoBot, enable a version (v2/v3/v4) via !启用会战, then use commands like !建会, !出刀, and !进度 which route through BattleMaster to persist data in SQLite and JSON files.
The 公会战 (clan battle) subsystem in ice9coffee/hoshinobot is implemented as a modular plugin under hoshino/modules/pcrclanbattle. It provides a complete guild management solution for Princess Connect! Re:Dive, handling everything from member registration to damage reporting and statistics generation.
Architecture of the Clan Battle Subsystem
The system follows a service-command-DAO pattern with three distinct layers:
- Service & Command Registration: Exposes bot commands and manages version selection via
version_selector.pyandclanbattle/__init__.py - Business Logic: Handles calculations, progress tracking, and statistics through
battlemaster.py - Persistence: Stores data in SQLite via DAO classes in
dao/sqlitedao.pyand JSON files for subscriptions
When a user sends a command prefixed with ! or !, the _clanbattle_bus listener in hoshino/modules/pcrclanbattle/clanbattle/__init__.py extracts the command name, looks it up in the module-level _registry dictionary, parses arguments using ArgParser, and dispatches to the handler coroutine. Handlers delegate all data operations to BattleMaster, which acts as a façade hiding DAO implementation details.
Enabling Specific Clan Battle Versions
HoshinoBot ships with three major clan battle implementations. Only one can be active per group at a time.
| Version | Entry Point | Characteristics |
|---|---|---|
| v2 | clanbattle/__init__.py |
Full command suite with comprehensive features |
| v3 | clanbattle_v3.py |
Simplified UI without web panel support |
| v4 | clanbattle_v4.py |
Post-June-2021 server support (web panel under development) |
To activate a version, a super-user executes the Version Selector command defined in hoshino/modules/pcrclanbattle/version_selector.py:
@sv.on_prefix('会战启用', '启用会战')
async def version_select(bot, ev: CQEvent):
gid = ev.group_id
arg = ev.message.extract_plain_text() # "v2", "v3", or "v4"
svs = Service.get_loaded_services()
# Enables selected version for this specific group only
Execute this in your group chat:
!启用会战 v2
The selector calls set_enable(gid) on the chosen service and disables others, making the command suite immediately available to that group.
Core Clan Battle Workflow
Once enabled, the standard operational workflow involves five key phases:
1. Clan and Member Management
Create a guild and add members using the administrative commands:
!建会 N星际舰队 Sjp # Creates "星际舰队" on JP server
!入会 小明 @123456789 # Adds member "小明" with QQ ID 123456789
Internally, !建会 triggers BattleMaster.add_clan(1, args.N, args.S) which inserts a row into the SQLite clan table with columns (gid, cid, name, server). The !入会 command calls BattleMaster.add_member(uid, qq_id, name, clan_id) to populate the member table.
2. Damage Reporting
Members report boss damage using the !出刀 command:
!出刀 12345 @987654321 R5 B3 # 12,345 damage on Round 5, Boss 3
The handler add_challenge in cmdv2.py builds a ParseResult dictionary and passes it to BattleMaster.process_challenge. This method:
- Retrieves current boss HP via
get_challenge_progress - Validates damage against remaining HP (auto-correcting over-damage by 30,000 threshold)
- Marks tail-cuts automatically when damage exceeds boss HP
- Persists the record via
BattleDao.addinto thebattletable with schema(gid, cid, yyyy, mm, eid, uid, alt, time, round, boss, dmg, flag)
3. Progress Monitoring
Check current boss status with:
!进度
This invokes BattleMaster.get_challenge_progress returning (round, boss, hp), then formats output via _gen_progress_text:
星际舰队 当前进度:
3周目 ③王 SCORE x1.2
HP=45,300/68,000
4. Subscriptions and Locking
Manage boss reservations and locks using:
!预约 3 M准备就绪 # Subscribe to Boss 3 with message "准备就绪"
!锁定 # Lock current boss for your clan
!挂树 # Report "hanging" status (waiting for rescue)
Subscription data is not stored in SQLite. Instead, SubscribeData class serializes reservations, locks, and hang-tree status to JSON files located at ~/.hoshino/clanbattle_sub/<group_id>.json using json.dump.
5. Statistics Generation
Generate visual reports with matplotlib:
!伤害统计 # Damage per member stacked bar chart
!分数统计 # Score statistics
The stat_damage method in BattleMaster pulls per-member damage arrays, matplotlib renders the chart, and util.fig2b64 converts it to base64 for message transmission.
Data Persistence Details
The subsystem uses a hybrid storage approach:
SQLite Database (~/.hoshino/clanbattle.db):
clantable: Guild configuration (gid,cid,name,server)membertable: Member roster (uid,alt,name,gid,cid)battletable: Challenge records with full damage history
JSON Files (~/.hoshino/clanbattle_sub/):
- Per-group subscription state for reservations (
预约), locks (锁定), and hang-tree status (挂树) - Accessed through
SubscribeDatahelper methods:add_sub,remove_sub,set_lock
DAO classes (ClanDao, MemberDao, BattleDao) in hoshino/modules/pcrclanbattle/clanbattle/dao/sqlitedao.py abstract raw SQL operations, providing type-safe CRUD methods like find_by(gid=..., cid=...) for member queries.
Extending and Customizing Features
To add new clan battle commands or modify workflows:
- Define argument parsing in
argparse/__init__.pyusingArgParserandArgHolderclasses to specify command syntax - Register the handler with the
@cb_cmd('命令名', ArgParser(...))decorator incmdv2.py(or the active version file) - Implement business logic by adding methods to
BattleMasterinbattlemaster.pyor calling existing façade methods - Extend persistence if needed by modifying
sqlitedao.pyto add new DAO classes or alter existing table schemas (requires manual database migration)
All privilege checks use priv.ADMIN constants, ensuring only group administrators or owners can execute destructive operations like !清空成员 or !删会.
Summary
- Enable the module per group using
!启用会战 v2(or v3/v4) viaversion_selector.py - Manage entities through
BattleMastermethods:add_clan,add_member,process_challenge - Store data in SQLite for permanent records (clans, members, battles) and JSON for transient state (subscriptions, locks)
- Extend functionality by registering new
@cb_cmdhandlers that delegate to the DAO layer via theBattleMasterfaçade - Key files:
hoshino/modules/pcrclanbattle/clanbattle/battlemaster.pyfor logic,hoshino/modules/pcrclanbattle/clanbattle/dao/sqlitedao.pyfor persistence, andhoshino/modules/pcrclanbattle/version_selector.pyfor version management
Frequently Asked Questions
How do I switch from clan battle v2 to v3 in HoshinoBot?
Send !启用会战 v3 in your group chat. The version_select handler in version_selector.py will disable the v2 service and enable v3 for that specific group ID. Only super-users can execute this command, and the change takes effect immediately without restarting the bot.
Where is clan battle data stored in HoshinoBot?
Permanent data resides in ~/.hoshino/clanbattle.db (SQLite) with three tables: clan, member, and battle. Temporary subscription data (reservations, locks, hang-tree status) is stored as JSON files in ~/.hoshino/clanbattle_sub/<group_id>.json. The sqlitedao.py file contains the DAO implementation handling all database operations.
What is the difference between !出刀 and process_challenge?
!出刀 is the user-facing chat command. When invoked, it triggers the add_challenge handler in cmdv2.py, which parses arguments and calls BattleMaster.process_challenge. The latter contains the business logic: validating damage against boss HP, detecting tail-cuts, and persisting the record via BattleDao.add. This separation ensures command parsing remains independent from battle calculation logic.
Can I modify the boss HP values for custom clan battles?
Yes. Edit hoshino/config_example/pcrclanbattle.py (or your active config directory) to adjust BOSS_HP and SCORE_RATE arrays. These values are loaded by BattleMaster during initialization. After modifying the configuration, restart the bot or reload the module for changes to take effect in damage calculations and progress tracking.
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 →