How to Configure and Use the Resource File System (RES_DIR, RES_PROTOCOL) in HoshinoBot
HoshinoBot uses two configuration variables, RES_DIR and RES_PROTOCOL, located in hoshino/config/__bot__.py, to control where static assets are stored and how they are delivered to OneBot/CQHTTP clients as either file paths, HTTP URLs, or Base64 data URIs.
The resource file system in HoshinoBot (a Python-based QQ bot framework) provides a secure, protocol-agnostic way to manage static assets like images, JSON data, and audio files. By configuring RES_DIR and RES_PROTOCOL, you control both the physical storage location and the transport method used when sending files to chat clients.
Understanding RES_DIR and RES_PROTOCOL Configuration
HoshinoBot defines resource behavior through three primary configuration variables in hoshino/config_example/__bot__.py:
| Variable | Purpose | Valid Values |
|---|---|---|
RES_DIR |
Absolute or relative path to the resource root directory | Any filesystem path (e.g., ./res/, ~/bot_assets/) |
RES_PROTOCOL |
Transport protocol for delivering assets to clients | "file" (default), "http", "base64" |
RES_URL |
Base HTTP URL for assets (required only when RES_PROTOCOL == "http") |
Valid URL ending with / (e.g., http://127.0.0.1:5000/static/) |
The configuration validation occurs in hoshino/config_example/__init__.py, which expands user directories and asserts protocol compatibility:
# hoshino/config_example/__init__.py
RES_DIR = os.path.expanduser(RES_DIR)
assert RES_PROTOCOL in ('http', 'file', 'base64')
To activate your configuration, rename the config_example directory to config so HoshinoBot loads these values at startup.
How the Resource System Works Internally
The core implementation resides in hoshino/R.py, which provides two wrapper classes for resource management:
ResObj– Generic file representation exposing apathproperty (absolute filesystem path) and optionalurlproperty (for HTTP protocol)ResImg– Specialized subclass that converts resources into CQ HTTP message segments via thecqcodeproperty
When you call R.img(), the system constructs a ResImg instance and generates the appropriate CQ code based on your RES_PROTOCOL:
# hoshino/R.py (simplified logic)
class ResImg(ResObj):
@property
def cqcode(self) -> MessageSegment:
if hoshino.config.RES_PROTOCOL == 'http':
return MessageSegment.image(self.url)
elif hoshino.config.RES_PROTOCOL == 'file':
return MessageSegment.image(f'file:///{os.path.abspath(self.path)}')
else: # base64
return MessageSegment.image(util.pic2b64(self.open()))
The module exposes two factory functions that automatically prepend RES_DIR and validate path safety:
R.get(*paths)– Returns aResObjfor arbitrary file typesR.img(*paths)– Returns aResImgready for chat transmission
Both functions enforce directory containment; attempting to traverse above RES_DIR raises a ValueError.
Step-by-Step Configuration Guide
Follow these steps to configure the resource file system for your deployment:
-
Create the resource directory at your preferred location (e.g.,
./res/or/opt/hoshino/res/). -
Copy and rename the configuration folder:
cp -r hoshino/config_example hoshino/config -
Edit
hoshino/config/__bot__.pyto set your protocol and directory:RES_PROTOCOL = 'file' # Options: file | http | base64 RES_DIR = r'./res/' # Use raw strings for Windows paths RES_URL = 'http://127.0.0.1:5000/static/' # Only for http protocol -
Restart HoshinoBot to load the updated configuration.
-
Verify access by placing a test image in your
RES_DIRand using the code examples below.
Practical Usage Examples
Sending Images with R.img()
The most common use case involves sending images stored in your resource directory:
from hoshino import R
# Sends ./res/img/pikachu.png using the configured RES_PROTOCOL
await bot.send(ev, R.img('img/pikachu.png').cqcode)
Combining Text and Resources
You can embed resource references within formatted messages:
msg = f"Boss Map: {R.img('raid/boss_map.jpg').cqcode}\nGood luck!"
await bot.send(ev, msg)
Reading Non-Image Files
For JSON configurations or text data, use R.get() to obtain the absolute filesystem path:
from hoshino import R
import json
config_path = R.get('data', 'gacha_config.json').path # Resolves to ./res/data/gacha_config.json
with open(config_path, encoding='utf-8') as f:
rates = json.load(f)
Serving Resources via HTTP
When running the bot and CQHTTP client on different machines, configure the HTTP protocol:
# hoshino/config/__bot__.py
RES_PROTOCOL = 'http'
RES_URL = 'http://192.168.1.100:8080/res/'
Ensure your web server (nginx, Flask, or FastAPI) serves the RES_DIR directory at the specified RES_URL path. HoshinoBot will then transmit full URLs instead of local file paths.
Embedding Base64 Data URIs
For standalone deployments without a file server, use Base64 encoding:
# hoshino/config/__bot__.py
RES_PROTOCOL = 'base64'
This setting causes R.img().cqcode to encode images as Base64 data URIs using util.pic2b64(), eliminating external HTTP dependencies at the cost of increased message payload size.
Summary
- Configuration files are located in
hoshino/config_example/__bot__.pyand validated in__init__.py - Three protocols are supported:
file(local filesystem),http(remote URL), andbase64(embedded data) - Core classes
ResObjandResImginhoshino/R.pyhandle path resolution and CQ code generation - Factory methods
R.get()andR.img()provide safe, validated access to resources while preventing directory traversal - Protocol switching requires only changing
RES_PROTOCOLand optionallyRES_URL, with no code changes needed in modules
Frequently Asked Questions
What is the difference between RES_PROTOCOL file and base64?
file sends a file:// URI referencing the local filesystem, requiring the bot and QQ client to run on the same machine with shared storage access. base64 embeds the entire image as a data URI, Works across network boundaries but increases network traffic by approximately 33% due to encoding overhead.
How do I configure RES_PROTOCOL http for remote servers?
Set RES_PROTOCOL = 'http' and define RES_URL as the externally accessible URL serving your RES_DIR directory. Deploy an HTTP server (such as nginx or Python's http.server) to host the resource folder at that URL, ensuring the path structure matches your local RES_DIR layout.
Why does R.img raise a ValueError?
HoshinoBot validates all resource paths to prevent directory traversal attacks. A ValueError indicates your path attempted to escape the RES_DIR root (e.g., using ../ sequences). Always use relative paths within the resource directory, such as R.img('subfolder/image.jpg').
Where should I place resource files for a plugin?
Create a subdirectory under RES_DIR (e.g., ./res/my_plugin/) and reference it using R.img('my_plugin/asset.png'). This keeps plugin assets organized and maintains compatibility with all three transport protocols without hardcoding absolute paths in your source code.
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 →