aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/hydrorollcore
diff options
context:
space:
mode:
Diffstat (limited to 'src/hydrorollcore')
-rw-r--r--src/hydrorollcore/__init__.py6
-rw-r--r--src/hydrorollcore/config.py77
-rw-r--r--src/hydrorollcore/core.py96
-rw-r--r--src/hydrorollcore/exceptions.py34
-rw-r--r--src/hydrorollcore/rule.py78
-rw-r--r--src/hydrorollcore/typing.py46
-rw-r--r--src/hydrorollcore/utils.py4
7 files changed, 74 insertions, 267 deletions
diff --git a/src/hydrorollcore/__init__.py b/src/hydrorollcore/__init__.py
index 13f83e86..93c042eb 100644
--- a/src/hydrorollcore/__init__.py
+++ b/src/hydrorollcore/__init__.py
@@ -1,6 +1,6 @@
from HydroRollCore.cli import Cli
-from HydroRollCore.config import ConfigModel
-from HydroRollCore.rule import Rule, RuleLoadType
+from HydroRollCore.config import Config
+from HydroRollCore.rule import Rule
from HydroRollCore.core import Core
-__all__ = ['Core', 'Rule', 'ConfigModel', 'RuleLoadType', 'Cli']
+__all__ = ['Core', 'Rule', 'Config', 'Cli']
diff --git a/src/hydrorollcore/config.py b/src/hydrorollcore/config.py
index 4bee29df..c7697d15 100644
--- a/src/hydrorollcore/config.py
+++ b/src/hydrorollcore/config.py
@@ -1,76 +1,7 @@
-"""HydroRollCore 配置。
+from pydantic import BaseModel
-HydroRollCore 使用 [pydantic](https://pydantic-docs.helpmanual.io/) 来读取配置。
-"""
-from typing import Set, Union
-from pydantic import Extra, Field, BaseModel, DirectoryPath
+class Config(BaseModel):
+ rule_dir: list = []
+ rules: list = []
-__all__ = [
- "ConfigModel",
- "LogConfig",
- "CoreConfig",
- "RuleConfig",
- "MainConfig",
-]
-
-
-class ConfigModel(BaseModel):
- """HydroRollCore 配置模型。
-
- Attributes:
- __config_name__: 配置名称。
- """
-
- __config_name__: str
-
- class Config:
- extra = Extra.allow
-
-
-class LogConfig(ConfigModel):
- """HydroRollCore 日志相关设置。
-
- Attributes:
- level: 日志级别。
- verbose_exception: 详细的异常记录,设置为 True 时会在日志中添加异常的 Traceback。
- """
-
- level: Union[str, int] = "DEBUG"
- verbose_exception: bool = False
-
-
-class CoreConfig(ConfigModel):
- """Core 配置。
-
- Attributes:
- rules: 将被加载的规则书列表,将被 `Core` 类的 `load_rules()` 方法加载。
- rule_dirs: 将被加载的规则书目录列表,将被 `Core` 类的 `load_rules_from_dirs()` 方法加载。
- log: HydroRollCore 日志相关设置。
- """
-
- rules: Set[str] = Field(default_factory=set)
- rule_dirs: Set[DirectoryPath] = Field(default_factory=set)
- log: LogConfig = LogConfig() # type: ignore
-
-
-class RuleConfig(ConfigModel):
- """规则书配置。"""
-
-
-class DebugConfig(ConfigModel):
- """是否打印事件配置。"""
-
-
-class MainConfig(ConfigModel):
- """HydroRollCore 配置。
-
- Attributes:
- core: HydroRollCore 的主要配置。
- """
-
- core: CoreConfig = CoreConfig() # type: ignore
- rule: RuleConfig = RuleConfig() # type: ignore
-
- class Config:
- extra = Extra.allow \ No newline at end of file
diff --git a/src/hydrorollcore/core.py b/src/hydrorollcore/core.py
index 9016eda8..f3c83989 100644
--- a/src/hydrorollcore/core.py
+++ b/src/hydrorollcore/core.py
@@ -1,76 +1,26 @@
-"""HydroRollCore 核心程序"""
+import importlib
+from typing import List
+from .exceptions import RuleLoadError
+from .rule import Rule
-import asyncio
-from collections import defaultdict
-import sys
-
-from typing import Any, List, Optional, Type, Dict
-
-from HydroRollCore.config import MainConfig
-from HydroRollCore.rule import Rule, RuleLoadType
-
-__all__ = ['Core']
class Core:
- """HydroRollCore 核心对象,定义了核心的基本方法。
- 读取并储存配置 `Config`,加载规则包 `Rule`,并进行事件分发。
-
- Attributes:
- config: 核心配置。
- should_exit: 核心是否应该进入准备退出状态。
- rules: 当前已经加载的规则包的列表。
- rules_priority_dict: 规则包优先级字典。
- rule_state: 规则包状态。
- global_state: 全局状态。
- """
-
- config: MainConfig
- should_exit: asyncio.Event
- rules: List[Rule]
- rules_priority_dict: Dict[int, List[Type[Rule]]]
- rule_state: Dict[str, Any]
- global_state: Dict[Any, Any]
-
- def __init__(
- self,
- *,
- config_file: Optional[str] = "config.toml",
- config_dict: Optional[Dict] = None,
- hot_reload: bool = False,
- ):
- """初始化 iamai ,读取配置文件,创建配置,加载规则包。
-
- Args:
- config_file: 配置文件,如不指定则使用默认的 `config.toml`。
- 若指定为 None,则不加载配置文件。
- config_dict: 配置字典,默认为 None。
- 若指定字典,则会忽略 config_file 配置,不再读取配置文件。
- hot_reload: 热重载。
- 启用后将自动检查 `rule_dir` 中的规则包文件更新,并在更新时自动重新加载。
- """
- self.config = MainConfig() # type: ignore[assignment]
- self.plugins_priority_dict = defaultdict(list)
- self.plugin_state = defaultdict(type(None)) # type: ignore[assignment]
- self.global_state = {}
-
- self.adapters = []
- self._restart_flag = False
- self._module_path_finder = ModulePathFinder()
- self._raw_config_dict = {}
-
- self._config_file = config_file
- self._config_dict = config_dict
- self._hot_reload = hot_reload
-
- self._extend_plugins = []
- self._extend_plugin_dirs = []
- self._extend_adapters = []
- self._bot_run_hooks = []
- self._bot_exit_hooks = []
- self._adapter_startup_hooks = []
- self._adapter_run_hooks = []
- self._adapter_shutdown_hooks = []
- self._event_preprocessor_hooks = []
- self._event_postprocessor_hooks = []
-
- sys.meta_path.insert(0, self._module_path_finder) \ No newline at end of file
+ def __init__(self, config):
+ self.rule_dir = config.rule_dir
+ self.rules = config.rules
+
+ async def load_rules(self) -> List[Rule]:
+ loaded_rules = []
+ for rule in self.rules:
+ try:
+ module = importlib.import_module(rule)
+ except ImportError as e:
+ raise RuleLoadError(f'Failed to load rule {rule}: {e}')
+ try:
+ rule_cls = getattr(module, rule.split('.')[-1])
+ if not issubclass(rule_cls, Rule):
+ raise RuleLoadError(f"Class '{rule_cls.__name__}' is not a subclass of 'Rule'")
+ except AttributeError as e:
+ raise RuleLoadError(f"Failed to get rule class from module '{rule}': {e}")
+ loaded_rules.append(rule_cls())
+ return loaded_rules
diff --git a/src/hydrorollcore/exceptions.py b/src/hydrorollcore/exceptions.py
index ae49fed2..a7cf2c0b 100644
--- a/src/hydrorollcore/exceptions.py
+++ b/src/hydrorollcore/exceptions.py
@@ -1,32 +1,2 @@
-"""HydroRollCore 异常。
-
-下列是 HydroRollCore 运行过程中可能会抛出的异常。这些异常大部分不需要用户处理,HydroRollCore 会自动捕获并处理。
-"""
-
-
-class EventException(BaseException):
- """事件处理过程中由插件抛出的异常,用于控制事件的传播,会被 iamai 自动捕获并处理。"""
-
-
-class SkipException(EventException):
- """跳过当前插件继续当前事件传播。"""
-
-
-class StopException(EventException):
- """停止当前事件传播。"""
-
-
-class iamaiException(Exception):
- """所有 iamai 发生的异常的基类。"""
-
-
-class GetEventTimeout(iamaiException):
- """当 get 方法超时使被抛出。"""
-
-
-class AdapterException(iamaiException):
- """由适配器抛出的异常基类,所有适配器抛出的异常都应该继承自此类。"""
-
-
-class LoadModuleError(iamaiException):
- """加载模块错误,在指定模块中找不到特定类型的类或模块中存在多个符合条件的类时抛出。""" \ No newline at end of file
+class RuleLoadError(Exception):
+ pass
diff --git a/src/hydrorollcore/rule.py b/src/hydrorollcore/rule.py
index e48b6ce2..6a89b68b 100644
--- a/src/hydrorollcore/rule.py
+++ b/src/hydrorollcore/rule.py
@@ -1,71 +1,17 @@
-from abc import ABC, abstractmethod
-import os
-import os.path
-from enum import Enum
-from iamai.config import ConfigModel
-from iamai.utils import is_config_class
-from typing import Generic, NoReturn, Optional, Type, TYPE_CHECKING
+from abc import ABCMeta, abstractmethod
-if TYPE_CHECKING:
- from iamai.bot import Bot
-
-
-class RuleLoadType(Enum):
- """插件加载类型。"""
-
- DIR = "dir"
- NAME = "name"
- FILE = "file"
- CLASS = "class"
-
-
-class Rule(ABC):
- """所有 iamai 插件的基类。
-
- Attributes:
- event: 当前正在被此插件处理的事件。
- priority: 插件的优先级,数字越小表示优先级越高,默认为 0。
- block: 插件执行结束后是否阻止事件的传播。True 表示阻止。
- __rule_load_type__: 插件加载类型,由 iamai 自动设置,反映了此插件是如何被加载的。
- __rule_file_path__: 当插件加载类型为 `RuleLoadType.CLASS` 时为 `None`,
- 否则为定义插件在的 Python 模块的位置。
- """
-
- priority: int = 0
- Config: Type[ConfigModel]
-
- __rule_load_type__: RuleLoadType
- __rule_file_path__: Optional[str]
-
- def __init__(self, event):
- self.event = event
-
- if not hasattr(self, "priority"):
- self.priority = 0
-
- self.get = self.bot.get
-
- self.__post_init__()
-
- def __post_init__(self):
- """用于初始化后处理,被 `__init__()` 方法调用。"""
+class Rule(metaclass=ABCMeta):
+ @abstractmethod
+ def __init__(self):
pass
- @property
- def name(self) -> str:
- """规则类名称。"""
- return self.__class__.__name__
-
- @property
- def bot(self) -> "Bot":
- """机器人对象。"""
- return self.event.adapter.bot
+ @classmethod
+ def __subclasshook__(cls, other):
+ if cls is Rule:
+ return hasattr(other, 'run') and callable(getattr(other, 'run'))
+ return NotImplemented
- @property
- def config(self):
- """规则包配置。"""
- config_class: ConfigModel = getattr(self, "Rule", None)
- if is_config_class(config_class):
- return getattr(self.config.rule, config_class.__config_name__, None)
- return None
+ @abstractmethod
+ async def run(self):
+ pass
diff --git a/src/hydrorollcore/typing.py b/src/hydrorollcore/typing.py
index 0b4a78ba..4a29d3c2 100644
--- a/src/hydrorollcore/typing.py
+++ b/src/hydrorollcore/typing.py
@@ -1,23 +1,33 @@
-"""HydroRollCore 类型提示支持。
+from pydantic import BaseModel
-此模块定义了部分 HydroRollCore 使用的类型。
-"""
-from typing import TYPE_CHECKING, TypeVar
+class Config(BaseModel):
+ rule_dir: list = []
+ rules: list = []
-if TYPE_CHECKING:
- from HydroRollCore.core import Core # noqa
- from HydroRollCore.rule import Rule # noqa
- from HydroRollCore.config import ConfigModel # noqa
-__all__ = [
- "T_State",
- "T_Core",
- "T_Rule",
- "T_Config"
-]
+class DiceConfig(BaseModel):
+ sides: int
+ counts: int
+ init_dice_pool: int
-T_State = TypeVar("T_State")
-T_Core = TypeVar("T_Core", bound="Core")
-T_Rule = TypeVar("T_Rule", bound="Rule")
-T_Config = TypeVar("T_Config", bound="ConfigModel") \ No newline at end of file
+
+class PlayerCard(BaseModel):
+ name: str
+ traits: list
+
+
+class Bonus(BaseModel):
+ level: int
+ cost: int
+
+
+class WikiPage(BaseModel):
+ title: str
+ content: str
+ tags: list = []
+
+
+class WikiModel:
+ class Setting(BaseModel):
+ desc: str
diff --git a/src/hydrorollcore/utils.py b/src/hydrorollcore/utils.py
index c69fdd48..71187856 100644
--- a/src/hydrorollcore/utils.py
+++ b/src/hydrorollcore/utils.py
@@ -15,7 +15,7 @@ from importlib.abc import MetaPathFinder
from importlib.machinery import PathFinder
from typing import Any, List, Type, Tuple, TypeVar, Callable, Iterable, Coroutine
-from HydroRollCore.config import ConfigModel
+from HydroRollCore.config import Config
__all__ = [
"ModulePathFinder",
@@ -55,7 +55,7 @@ def is_config_class(config_class: Any) -> bool:
"""
return (
inspect.isclass(config_class)
- and issubclass(config_class, ConfigModel)
+ and issubclass(config_class, Config)
and isinstance(getattr(config_class, "__config_name__", None), str)
and ABC not in config_class.__bases__
and not inspect.isabstract(config_class)