Files
HeXi/hexi/plugins/nonebot_plugin_group_daily_analysis/config.py
T
sansenhoshiandClaude b61d09f09f Add HeXi bot codebase: custom plugins, web frontends, tests
- hexi core: message handling, rate limiting, cooldown, plugin manager
- Custom plugins: BF stats, daily check-in, quotes, persona cards, etc.
- Community plugins vendored under hexi/plugins with local fixes
- Web admin frontends (learning-chat, persona-admin), unified hexi/web
- Tests for rate_limit/cooldown/memes/persona; poetry.lock

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-01 13:13:40 +08:00

143 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""NoneBot 版插件配置加载。
配置持久化在数据目录 config.json,键值结构与原 AstrBot 配置分组一致,
这样可以直接喂给 core 的 ConfigManager(它本质只读嵌套 dict)。
"""
from __future__ import annotations
import json
import os
from pathlib import Path
from .core.shared.constants import PLUGIN_NAME
from .core.utils.paths import get_data_dir
class ConfigDict(dict):
"""兼容原 ConfigManager 的 dict;save_config 会把改动持久化到 config.json。"""
def save_config(self) -> None:
try:
config_file().write_text(
json.dumps(dict(self), ensure_ascii=False, indent=2, default=str),
encoding="utf-8",
)
except OSError:
pass
def _build_from_items(items: dict) -> dict:
"""从 schema 的 items 递归生成嵌套默认配置。"""
out: dict = {}
for key, item in items.items():
if not isinstance(item, dict):
continue
if item.get("type") == "object" and isinstance(item.get("items"), dict):
out[key] = _build_from_items(item["items"])
elif "default" in item:
out[key] = item["default"]
return out
def _schema_defaults() -> dict:
"""从插件的 _conf_schema.json 读取完整默认配置。"""
schema_path = Path(__file__).resolve().parents[0] / "_conf_schema.json"
try:
schema = json.loads(schema_path.read_text(encoding="utf-8-sig"))
except (OSError, json.JSONDecodeError):
return {}
defaults: dict = {}
for group, spec in schema.items():
if isinstance(spec, dict) and isinstance(spec.get("items"), dict):
defaults[group] = _build_from_items(spec["items"])
return defaults
def _read_config_value(*names: str, default: str = "") -> str:
"""依次从 shell 环境变量与 NoneBot driver.config(.env) 读取值。
NoneBot 的 .env 是通过 pydantic 加载的,不会写入 os.environ,
因此必须能从 get_driver().config 的小写属性读取。优先环境变量。
"""
for n in names:
v = os.environ.get(n)
if v:
return str(v)
try:
from nonebot import get_driver
drv_cfg = get_driver().config
except Exception:
return default
for n in names:
attr = n.lower()
try:
v = getattr(drv_cfg, attr, None)
except Exception:
v = None
if v:
return str(v)
return default
def _llm_env_values() -> dict:
"""用户必填的 LLM 端点/密钥/模型;AstrBot 版是 Provider 体系,插件本身不存这些。"""
return {
"llm_api_base": _read_config_value("HEXI_LLM_API_BASE"),
"llm_api_key": _read_config_value("HEXI_LLM_API_KEY"),
"llm_model": _read_config_value("HEXI_LLM_MODEL"),
}
def _default_config() -> dict:
cfg = _schema_defaults()
cfg.setdefault("llm", {}).update(_llm_env_values())
cfg.setdefault("basic", {}).setdefault("output_format", ["image"])
cfg.setdefault("basic", {}).setdefault("enable_base64_image", True)
cfg.setdefault("basic", {}).setdefault("analysis_days", 1)
cfg.setdefault("basic", {}).setdefault("max_messages", 1000)
cfg.setdefault("basic", {}).setdefault("min_messages_threshold", 50)
cfg.setdefault("basic", {}).setdefault("filter_bot_messages", True)
cfg.setdefault("analysis_features", {}).setdefault("chat_quality_analysis_enabled", False)
cfg.setdefault("incremental", {}).setdefault("incremental_enabled", False)
return cfg
def config_file() -> Path:
return get_data_dir(PLUGIN_NAME) / "config.json"
def load_config() -> ConfigDict:
cfg = ConfigDict(_default_config())
f = config_file()
if f.exists():
try:
user = json.loads(f.read_text(encoding="utf-8"))
for group, values in user.items():
if isinstance(values, dict):
cfg.setdefault(group, {}).update(values)
else:
cfg[group] = values
except (OSError, json.JSONDecodeError) as e:
_log_warn(f"读取配置文件失败: {e}")
# 环境变量/NoneBot .env 永远优先于持久化 config.json,避免被空值覆盖
cfg.setdefault("llm", {}).update(_llm_env_values())
return cfg
def save_config(cfg: dict) -> None:
try:
config_file().write_text(
json.dumps(cfg, ensure_ascii=False, indent=2), encoding="utf-8"
)
except OSError as e:
_log_warn(f"保存配置文件失败: {e}")
def _log_warn(msg: str) -> None:
try:
from .core.utils.logger import logger
logger.warning(msg)
except Exception:
pass