|
18 | 18 | logger = logging.getLogger("astrbot") |
19 | 19 |
|
20 | 20 |
|
| 21 | +def _is_config_number(value) -> bool: |
| 22 | + return isinstance(value, (int, float)) and not isinstance(value, bool) |
| 23 | + |
| 24 | + |
| 25 | +_SCHEMA_TYPE_VALIDATORS = { |
| 26 | + "int": lambda v: isinstance(v, int) and not isinstance(v, bool), |
| 27 | + "float": _is_config_number, |
| 28 | + "bool": lambda v: isinstance(v, bool), |
| 29 | + "string": lambda v: isinstance(v, str), |
| 30 | + "text": lambda v: isinstance(v, str), |
| 31 | + "list": lambda v: isinstance(v, list), |
| 32 | + "file": lambda v: isinstance(v, list), |
| 33 | + "object": lambda v: isinstance(v, dict), |
| 34 | + "dict": lambda v: isinstance(v, dict), |
| 35 | + "template_list": lambda v: isinstance(v, list), |
| 36 | +} |
| 37 | + |
| 38 | + |
| 39 | +def _validate_schema_default(field: str, typ: str, default) -> None: |
| 40 | + if not _SCHEMA_TYPE_VALIDATORS[typ](default): |
| 41 | + raise TypeError(f"配置项 {field} 的 default 与类型 {typ} 不匹配") |
| 42 | + |
| 43 | + |
| 44 | +def _validate_schema_slider(field: str, typ: str, slider: dict) -> None: |
| 45 | + if typ not in ("int", "float"): |
| 46 | + raise TypeError(f"配置项 {field} 只有 int/float 类型支持 slider") |
| 47 | + if not isinstance(slider, dict) or not all( |
| 48 | + _is_config_number(slider.get(key)) for key in ("min", "max", "step") |
| 49 | + ): |
| 50 | + raise TypeError( |
| 51 | + f"配置项 {field} 的 slider 必须包含数字 min/max/step", |
| 52 | + ) |
| 53 | + |
| 54 | + |
| 55 | +def _validate_config_schema_item(field: str, item: dict) -> None: |
| 56 | + typ = item["type"] |
| 57 | + if typ not in DEFAULT_VALUE_MAP: |
| 58 | + raise TypeError( |
| 59 | + f"不受支持的配置类型 {typ}。支持的类型有:{DEFAULT_VALUE_MAP.keys()}", |
| 60 | + ) |
| 61 | + if "options" in item and not isinstance(item["options"], list): |
| 62 | + raise TypeError(f"配置项 {field} 的 options 必须是列表") |
| 63 | + if "obvious_hint" in item and not isinstance(item["obvious_hint"], bool): |
| 64 | + raise TypeError(f"配置项 {field} 的 obvious_hint 必须是布尔值") |
| 65 | + if "slider" in item: |
| 66 | + _validate_schema_slider(field, typ, item["slider"]) |
| 67 | + if typ == "object" and not isinstance(item.get("items"), dict): |
| 68 | + raise TypeError(f"配置项 {field} 的 items 必须是对象") |
| 69 | + default = item["default"] if "default" in item else DEFAULT_VALUE_MAP[typ] |
| 70 | + _validate_schema_default(field, typ, default) |
| 71 | + if typ == "object": |
| 72 | + for child_key, child_item in item["items"].items(): |
| 73 | + _validate_config_schema_item(f"{field}.{child_key}", child_item) |
| 74 | + |
| 75 | + |
21 | 76 | class RateLimitStrategy(enum.Enum): |
22 | 77 | STALL = "stall" |
23 | 78 | DISCARD = "discard" |
@@ -133,10 +188,7 @@ def _config_schema_to_default_config(self, schema: dict) -> dict: |
133 | 188 |
|
134 | 189 | def _parse_schema(schema: dict, conf: dict) -> None: |
135 | 190 | for k, v in schema.items(): |
136 | | - if v["type"] not in DEFAULT_VALUE_MAP: |
137 | | - raise TypeError( |
138 | | - f"不受支持的配置类型 {v['type']}。支持的类型有:{DEFAULT_VALUE_MAP.keys()}", |
139 | | - ) |
| 191 | + _validate_config_schema_item(k, v) |
140 | 192 | if "default" in v: |
141 | 193 | default = v["default"] |
142 | 194 | else: |
|
0 commit comments