How to Implement Arena Query Functionality in HoshinoBot: Complete API Integration Guide
The arena query feature in HoshinoBot integrates with the pcrdfans API to fetch Princess Connect! Re:Dive attack team recommendations, utilizing a three-layer architecture spanning command handlers in __init__.py, asynchronous business logic in arena.py, and local JSON persistence for user feedback.
The arena query module in the ice9coffee/hoshinobot repository provides a production-ready example of external API integration for gaming bots. This guide explains how to implement arena query functionality in HoshinoBot, covering the complete flow from user command input through rate limiting and image-based response rendering.
Architecture Overview
The implementation organizes functionality across three distinct layers to separate concerns and maintain maintainability:
Command and Service Layer (hoshino/modules/priconne/arena/__init__.py): Registers bot commands, validates user input, enforces rate limits, and formats replies using image generation utilities.
Business and Query Layer (hoshino/modules/priconne/arena/arena.py): Constructs authenticated API requests to the external pcrdfans service, parses JSON responses, generates quick-keys for result identification, and enriches data with local user feedback.
Persistence Layer (arena.py): Maintains per-user likes and dislikes in a local JSON file located at ~/.hoshino/arena_db.json, enabling persistent feedback across bot restarts.
Command Registration and Service Layer
The module initializes a Service object to register commands and manage bot lifecycle events. Four distinct command prefixes handle region-specific queries for Princess Connect! Re:Dive servers:
怎么拆(Mainland CN, region=1)b怎么拆(Bilibili CN, region=2)台怎么拆(Taiwan, region=3)日怎么拆(Japan, region=4)
Each prefix maps to the core _arena_query function with the appropriate region code. According to lines 38-52 of hoshino/modules/priconne/arena/__init__.py, the registration pattern uses decorators:
@sv.on_prefix(aliases) # CN
async def arena_query(bot, ev):
await _arena_query(bot, ev, region=1)
@sv.on_prefix(aliases_b) # B-CN
async def arena_query_b(bot, ev):
await _arena_query(bot, ev, region=2)
The region integer is forwarded directly to the API, allowing the external service to filter results by server without requiring additional logic changes in the bot code.
Input Validation and Rate Limiting
The _arena_query function implements defensive programming to prevent abuse and ensure data quality. First, it instantiates a FreqLimiter configured to a 5-second cooldown per user:
if not lmt.check(uid):
await bot.finish(ev, '您查询得过于频繁,请稍等片段', at_sender=True)
lmt.start_cd(uid)
After passing the rate limit, the function extracts plain text from the message event, strips punctuation using filt_message, and converts character names or IDs into standardized integer IDs using the shared chara utilities. Validation steps include verifying team length constraints, checking for duplicate characters, and filtering out invalid NPC identifiers before proceeding to the API call (lines 81-107 of __init__.py).
API Integration and Business Logic
The core API interaction resides in the do_query function within arena.py. This implementation demonstrates robust patterns for authenticated external service communication.
Authentication and Payload Construction
The module retrieves the AUTH_KEY from the global configuration at config.priconne.arena.AUTH_KEY, which operators must define in hoshino/config/priconne.py (copied from hoshino/config_example/priconne.py). The authentication header and request payload follow the pcrdfans API specification:
def __get_auth_key():
return config.priconne.arena.AUTH_KEY
# In do_query:
payload = {
"_sign": "a",
"def": [id * 100 + 1 for id in def_team_ids], # API-specific encoding
"nonce": "a",
"page": 1,
"sort": 1,
"ts": int(time.time()),
"region": region,
}
The defense team IDs are multiplied by 100 and incremented by 1 to match the API's expected format.
Asynchronous Request Handling
The module uses aiorequests.post (an aiohttp wrapper defined in hoshino/aiorequests.py) to send non-blocking HTTP requests with a 10-second timeout:
resp = await aiorequests.post(
"https://api.pcrdfans.com/x/v1/search",
headers={"Authorization": __get_auth_key()},
json=payload,
timeout=10,
)
This async pattern ensures the bot remains responsive to other events while awaiting the external API response.
Response Parsing and Quick-Key Generation
Upon receiving the response, the code verifies the status code (res["code"] == 0) before processing. For each attack team recommendation, the system:
- Generates a quick-key via
gen_quick_key— a 5-character base-32 string that maps to the true arena solution ID - Converts raw character IDs into
charaobjects usingchara.fromidfor image rendering - Aggregates like/dislike counts from the local
arena_db.jsondatabase
The quick-key system (lines 93-100 of arena.py) enables users to reference specific attack teams using short codes (e.g., AB3DE) without exposing internal database identifiers.
Rendering Results and Handling Feedback
After retrieving up to six results, _arena_query passes the data to render_atk_def_teams, which generates a composite image showing defense teams, recommended attacks, vote counts, and quick-keys. The image is base-64 encoded using pic2b64 and transmitted via MessageSegment.image (lines 35-44 of __init__.py).
Users provide feedback using the commands 点赞+<quick-key> (like) or 点踩+<quick-key> (dislike). The _arena_feedback handler validates the 5-character key, resolves it to the true ID using arena.get_true_id, updates the in-memory database via add_like or add_dislike, and persists changes immediately with dump_db to ~/.hoshino/arena_db.json (lines 86-98 of __init__.py).
Configuration Requirements
To activate the arena query functionality, operators must configure the authentication key:
- Copy
hoshino/config_example/priconne.pytohoshino/config/priconne.py - Replace the placeholder in the arena class:
class arena:
AUTH_KEY = "your_pcrdfans_api_key_here"
Obtain the API key from the pcrdfans developer portal. Without this configuration, the do_query function will fail to authenticate with the external service.
Extending the Implementation
You can adapt this module for different data sources or additional game servers.
Integrating a Custom API Endpoint
To point the bot at a different arena database, modify the URL constant in hoshino/modules/priconne/arena/arena.py:
# Replace the default pcrdfans endpoint
API_URL = "https://my.custom.service/arena/search"
resp = await aiorequests.post(API_URL, headers=header, json=payload, timeout=10)
The payload structure remains compatible with any REST endpoint accepting the same JSON schema.
Adding Support for New Regions
To support a new region (e.g., Korea as region=5), extend the command aliases in __init__.py:
aliases_kr = ('韩怎么拆', 'kr怎么拆')
@sv.on_prefix(aliases_kr)
async def arena_query_kr(bot, ev):
await _arena_query(bot, ev, region=5)
No modifications to arena.do_query are required, as the region parameter passes through transparently to the API payload.
Summary
- The arena module implements a clean separation between command handling (
__init__.py), API business logic (arena.py), and JSON persistence. - FreqLimiter enforces 5-second cooldowns per user to prevent API spam, while quick-keys provide a compact addressing scheme for user feedback.
- The
do_queryfunction constructs authenticated POST requests tohttps://api.pcrdfans.com/x/v1/searchusingaiorequests.post, handling async I/O without blocking the bot event loop. - Configuration requires only the
AUTH_KEYinhoshino/config/priconne.py, making deployment straightforward for operators with valid API credentials.
Frequently Asked Questions
How do I obtain the API key required for the arena query?
Register at the pcrdfans developer portal to receive an authentication key. Place this key in hoshino/config/priconne.py within the arena.AUTH_KEY variable, following the structure shown in hoshino/config_example/priconne.py. The bot loads this configuration at startup to authenticate all API requests.
Can I replace the pcrdfans API with a custom arena database?
Yes. Modify the URL string in the do_query function within hoshino/modules/priconne/arena/arena.py. The module uses standard aiorequests.post with a JSON payload containing defense teams, region codes, and timestamps, making it compatible with any REST endpoint implementing a similar search interface. Ensure your custom API returns responses in the expected format with code, data, and result entries.
What is the purpose of the quick-key system?
The quick-key is a 5-character base-32 string generated by gen_quick_key that acts as a short alias for the true arena solution ID. This abstraction allows users to reference specific attack team recommendations using compact codes like AB3DE when issuing 点赞 (like) or 点踩 (dislike) commands. The mapping is maintained in memory during runtime and resolved via get_true_id when processing feedback.
How do I add support for additional server regions?
Define new command alias tuples in hoshino/modules/priconne/arena/__init__.py and bind them to @sv.on_prefix decorators that invoke _arena_query with the appropriate region integer (e.g., region=5 for a new server). The do_query function forwards this region code directly to the API payload without requiring changes to the core query logic, provided the external service supports the new region identifier.
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 →