From 72fbff3005ca6caa5c02272bc7b7d8aeb67a37df Mon Sep 17 00:00:00 2001 From: lingyun14 Date: Sun, 12 Jul 2026 21:23:44 +0800 Subject: [PATCH 1/2] fix: accept UTF-8 BOM in plugin schemas --- astrbot/core/star/star_manager.py | 31 +++++++++++++++++++++++-------- tests/test_plugin_manager.py | 26 ++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/astrbot/core/star/star_manager.py b/astrbot/core/star/star_manager.py index 38bdc61d71..45be476d2f 100644 --- a/astrbot/core/star/star_manager.py +++ b/astrbot/core/star/star_manager.py @@ -575,6 +575,22 @@ def _load_plugin_i18n(plugin_path: str) -> dict[str, dict]: return translations + @staticmethod + def _load_plugin_config_schema(schema_path: str) -> dict: + """Load a plugin config schema, accepting an optional UTF-8 BOM.""" + try: + with open(schema_path, encoding="utf-8-sig") as f: + return json.load(f) + except UnicodeDecodeError as exc: + raise ValueError( + f"插件配置 schema 必须使用 UTF-8 编码: {schema_path}" + ) from exc + except json.JSONDecodeError as exc: + raise ValueError( + f"插件配置 schema 不是有效的 JSON: {schema_path} " + f"(line {exc.lineno}, column {exc.colno})" + ) from exc + @staticmethod def _normalize_plugin_dir_name(plugin_name: str) -> str: return plugin_name.strip() @@ -1111,14 +1127,13 @@ async def load( ) if os.path.exists(plugin_schema_path): # 加载插件配置 - with open(plugin_schema_path, encoding="utf-8") as f: - plugin_config = AstrBotConfig( - config_path=os.path.join( - self.plugin_config_path, - f"{root_dir_name}_config.json", - ), - schema=json.loads(f.read()), - ) + plugin_config = AstrBotConfig( + config_path=os.path.join( + self.plugin_config_path, + f"{root_dir_name}_config.json", + ), + schema=self._load_plugin_config_schema(plugin_schema_path), + ) logo_path = os.path.join(plugin_dir_path, self.logo_fname) if path in star_map: diff --git a/tests/test_plugin_manager.py b/tests/test_plugin_manager.py index e51f9f35a1..56b01b4c26 100644 --- a/tests/test_plugin_manager.py +++ b/tests/test_plugin_manager.py @@ -20,6 +20,32 @@ TEST_PLUGIN_DIR = "helloworld" +def test_load_plugin_config_schema_accepts_utf8_bom(tmp_path: Path): + schema_path = tmp_path / "_conf_schema.json" + schema_path.write_bytes(b'\xef\xbb\xbf{"type": "object"}') + + assert PluginManager._load_plugin_config_schema(str(schema_path)) == { + "type": "object" + } + + +def test_load_plugin_config_schema_accepts_utf8_without_bom(tmp_path: Path): + schema_path = tmp_path / "_conf_schema.json" + schema_path.write_text('{"type": "object"}', encoding="utf-8") + + assert PluginManager._load_plugin_config_schema(str(schema_path)) == { + "type": "object" + } + + +def test_load_plugin_config_schema_reports_invalid_json(tmp_path: Path): + schema_path = tmp_path / "_conf_schema.json" + schema_path.write_text("{invalid", encoding="utf-8") + + with pytest.raises(ValueError, match="不是有效的 JSON"): + PluginManager._load_plugin_config_schema(str(schema_path)) + + class MockStar: def __init__(self): self.root_dir_name = TEST_PLUGIN_DIR From 52d5c8f6703dec3c423c9e5753a9f58daf14a93c Mon Sep 17 00:00:00 2001 From: lingyun14 Date: Sun, 12 Jul 2026 21:57:37 +0800 Subject: [PATCH 2/2] fix: accept UTF-8 BOM in plugin i18n --- astrbot/core/star/star_manager.py | 2 +- tests/test_plugin_manager.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/astrbot/core/star/star_manager.py b/astrbot/core/star/star_manager.py index 45be476d2f..bd31d5df2a 100644 --- a/astrbot/core/star/star_manager.py +++ b/astrbot/core/star/star_manager.py @@ -559,7 +559,7 @@ def _load_plugin_i18n(plugin_path: str) -> dict[str, dict]: continue try: - with file_path.open(encoding="utf-8") as f: + with file_path.open(encoding="utf-8-sig") as f: locale_data = json.load(f) if isinstance(locale_data, dict): translations[locale] = locale_data diff --git a/tests/test_plugin_manager.py b/tests/test_plugin_manager.py index 56b01b4c26..d6bf22ee19 100644 --- a/tests/test_plugin_manager.py +++ b/tests/test_plugin_manager.py @@ -85,9 +85,11 @@ def test_load_plugin_i18n_reads_locale_files(tmp_path: Path): plugin_path = tmp_path / "plugin" i18n_path = plugin_path / ".astrbot-plugin" / "i18n" i18n_path.mkdir(parents=True) - (i18n_path / "zh-CN.json").write_text( - json.dumps({"metadata": {"desc": "中文描述"}}, ensure_ascii=False), - encoding="utf-8", + (i18n_path / "zh-CN.json").write_bytes( + b"\xef\xbb\xbf" + + json.dumps({"metadata": {"desc": "中文描述"}}, ensure_ascii=False).encode( + "utf-8" + ), ) (i18n_path / "en-US.json").write_text( json.dumps({"metadata": {"desc": "English description"}}),