aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/hydroroll
diff options
context:
space:
mode:
author简律纯 <i@jyunko.cn>2023-11-03 03:28:35 +0800
committer简律纯 <i@jyunko.cn>2023-11-03 03:28:35 +0800
commit5a2033860a328c4116f0ede2874915315e7487b0 (patch)
treef7b6d88d50ce33bd5fb52fbcfbca906738f412d6 /hydroroll
parent4bf6db5200affc2f623aa02301020092c0789d19 (diff)
downloadHydroRoll-5a2033860a328c4116f0ede2874915315e7487b0.tar.gz
HydroRoll-5a2033860a328c4116f0ede2874915315e7487b0.zip
Co-authored-by: HadalFauna <HadalFauna@users.noreply.github.com>
Diffstat (limited to 'hydroroll')
-rw-r--r--hydroroll/__init__.py221
-rw-r--r--hydroroll/config.py80
-rw-r--r--hydroroll/core/__init__.py70
-rw-r--r--hydroroll/exceptions.py0
-rw-r--r--hydroroll/psi/.gitkeep0
-rw-r--r--hydroroll/typing.py34
-rw-r--r--hydroroll/utils.py97
7 files changed, 0 insertions, 502 deletions
diff --git a/hydroroll/__init__.py b/hydroroll/__init__.py
deleted file mode 100644
index b064bca..0000000
--- a/hydroroll/__init__.py
+++ /dev/null
@@ -1,221 +0,0 @@
-from collections import defaultdict
-from itertools import chain
-from pathlib import Path
-from typing import Dict, List, Optional, Type, Union
-from iamai import ConfigModel, Plugin
-from iamai.log import logger, error_or_exception
-from iamai.utils import ModulePathFinder, get_classes_from_module_name, get_classes_from_dir, get_classes_from_module
-from .config import GlobalConfig
-from iamai.exceptions import LoadModuleError
-from .utils import BasePlugin, RegexPluginBase, CommandPluginBase
-import os
-# from .core import Rule, RuleLoadType
-from HydroRollCore import Rule, RuleLoadType
-
-class HydroRoll(Plugin):
-
- class Config(ConfigModel):
- __config_name__ = "HydroRoll"
-
- priority = 0
- rules_priority_dict: Dict[int, List[Type[Rule]]] = defaultdict(list)
- _module_path_finder = ModulePathFinder()
-
-
- def _load_rule_class(
- self,
- rule_class: Type[Rule],
- rule_load_type: RuleLoadType,
- rule_file_path: Optional[str],
- ):
- logger.info(f'Loading rule from class "{rule_class!r}"')
- """加载规则类。"""
- priority = getattr(rule_class, "priority", None)
- if type(priority) is int and priority >= 0:
- for _rule in self.rules:
- if _rule.__name__ == rule_class.__name__:
- logger.warning(
- f'Already have a same name rule "{_rule.__name__}"'
- )
- rule_class.__rule_load_type__ = rule_load_type
- rule_class.__rule_file_path__ = rule_file_path
- self.rules_priority_dict[priority].append(rule_class)
- logger.success(
- f'Succeeded to load rule "{rule_class.__name__}" '
- f'from class "{rule_class!r}"'
- )
- else:
- error_or_exception(
- f'Load rule from class "{rule_class!r}" failed:',
- LoadModuleError(
- f'Rule priority incorrect in the class "{rule_class!r}"'
- ),
- self.bot.config.bot.log.verbose_exception,
- )
-
- def _load_rules_from_module_name(
- self, module_name: str, rule_load_type: RuleLoadType
- ):
- logger.info(f'Loading rules from module "{module_name}"')
- """从模块名称中规则包模块。"""
- try:
- rule_classes = get_classes_from_module_name(module_name, Rule)
- except ImportError as e:
- error_or_exception(
- f'Import module "{module_name}" failed:',
- e,
- self.bot.config.bot.log.verbose_exception,
- )
- else:
- for rule_class, module in rule_classes:
- self._load_rule_class(
- rule_class,
- rule_load_type,
- module.__file__,
- )
-
- def _load_rules(
- self,
- *rules: Union[Type[Plugin], str, Path],
- rule_load_type: Optional[RuleLoadType] = None,
- ):
- """加载规则包。
-
- Args:
- *rules: 规则类、规则包模块名称或者规则包模块文件路径。类型可以是 `Type[Rule]`, `str` 或 `pathlib.Path`。
- 如果为 `Type[Rule]` 类型时,将作为规则类进行加载。
- 如果为 `str` 类型时,将作为规则包模块名称进行加载,格式和 Python `import` 语句相同。
- 例如:`path.of.rule`。
- 如果为 `pathlib.Path` 类型时,将作为规则包模块文件路径进行加载。
- 例如:`pathlib.Path("path/of/rule")`。
- rule_load_type: 规则加载类型,如果为 None 则自动判断,否则使用指定的类型。
- """
- logger.info("Loading rules...")
- for rule_ in rules:
- if isinstance(rule_, type):
- if issubclass(rule_, Rule):
- self._load_rule_class(
- rule_, rule_load_type or RuleLoadType.CLASS, None
- )
- else:
- logger.error(
- f'The rule class "{rule_!r}" must be a subclass of Rule'
- )
- elif isinstance(rule_, str):
- logger.warning(f'Loading rules from module "{rule_}"')
- self._load_rules_from_module_name(
- rule_, rule_load_type or RuleLoadType.NAME
- )
- elif isinstance(rule_, Path):
- logger.warning(f'Loading rules from path "{rule_}"')
- if rule_.is_file():
- if rule_.suffix != ".py":
- logger.error(f'The path "{rule_}" must endswith ".py"')
- return
-
- rule_module_name = None
- for path in self._module_path_finder.path:
- try:
- if rule_.stem == "__init__":
- if rule_.resolve().parent.parent.samefile(Path(path)):
- rule_module_name = rule_.resolve().parent.name
- break
- elif rule_.resolve().parent.samefile(Path(path)):
- rule_module_name = rule_.stem
- break
- except OSError:
- continue
- if rule_module_name is None:
- rel_path = rule_.resolve().relative_to(Path(".").resolve())
- if rel_path.stem == "__init__":
- rule_module_name = ".".join(rel_path.parts[:-1])
- else:
- rule_module_name = ".".join(
- rel_path.parts[:-1] + (rel_path.stem,)
- )
-
- self._load_rules_from_module_name(
- rule_module_name, rule_load_type or RuleLoadType.FILE
- )
- else:
- logger.error(f'The rule path "{rule_}" must be a file')
- else:
- logger.error(f"Type error: {rule_} can not be loaded as plugin")
-
- def load_rules(self, *rules: Union[Type[Rule], str, Path]):
- """加载规则。
-
- Args:
- *rules: 规则类、规则包x模块名称或者规则包模块文件路径。类型可以是 `Type[Rule]`, `str` 或 `pathlib.Path`。
- 如果为 `Type[Rule]` 类型时,将作为规则类进行加载。
- 如果为 `str` 类型时,将作为规则包模块名称进行加载,格式和 Python `import` 语句相同。
- 例如:`path.of.rule`。
- 如果为 `pathlib.Path` 类型时,将作为规则包模块文件路径进行加载。
- 例如:`pathlib.Path("path/of/rule")`。
- """
- # self._extend_rules.extend(rules)
- return self._load_rules(*rules)
-
- def _load_rules_from_dirs(self, *dirs: Path):
- """从目录中加载规则包,以 `_` 开头的模块中的规则不会被导入。路径可以是相对路径或绝对路径。
-
- Args:
- *dirs: 储存包含规则的模块的模块路径。
- 例如:`pathlib.Path("path/of/rules/")` 。
- """
- logger.info("Loading rules from dirs...")
- dirs = list(map(lambda x: str(x.resolve()), dirs)) # type: ignore maybe remove?
- logger.warning(f'Loading rules from dirs "{", ".join(map(str, dirs))}"')
- self._module_path_finder.path.extend(dirs) # type: ignore
- # type: ignore
- for rule_class, module in get_classes_from_dir(dirs, Rule): # type: ignore
- self._load_rule_class(rule_class, RuleLoadType.DIR, module.__file__)
-
- def load_rules_from_dirs(self, *dirs: Path):
- """从目录中加载规则,以 `_` 开头的模块中的规则不会被导入。路径可以是相对路径或绝对路径。
-
- Args:
- *dirs: 储存包含rule的模块的模块路径。
- 例如:`pathlib.Path("path/of/rules/")` 。
- """
- # self._extend_rule_dirs.extend(dirs)
- self._load_rules_from_dirs(*dirs)
-
- @property
- def rules(self) -> List[Type[Plugin]]:
- """当前已经加载的规则包的列表。"""
- return list(chain(*self.rules_priority_dict.values()))
-
- def __post_init__(self):
- if not self.bot.global_state.get('init', False):
- self.bot.global_state = dict()
- self.bot.global_state['init'] = True
-
- self._load_rules_from_dirs(Path(os.path.join("\\".join(os.path.dirname(__file__).split('\\')[:-2]),"rules"))) #*self.config.rule['rule_dirs'])
- # self._load_rules(*self.config.rule.rules)
-
- ...
-
- async def handle(self) -> None:
- """
- @TODO: HydroRollCore should be able to handle all signals and tokens from Psi.
- @BODY: HydroRollCore actives the rule-packages.
- """
-
- if self.event.message.get_plain_text() == '.core':
- await self.event.reply("HydroRollCore is running.")
- elif self.event.message.startswith('.show'):
- try:
- await self.event.reply(eval(self.event.message.get_plain_text()[6:]))
- except Exception as e:
- await self.event.reply(f"{e!r}")
-
- async def rule(self) -> bool:
- """
- @TODO: Psi should be able to handle all message first.
- @BODY: lexer module will return a list of tokens, parser module will parse the tokens into a tree, and executor module will execute the tokens with a stack with a bool return value.
- """
-
- if self.event.type != "message":
- return False
- return self.event.message.get_plain_text().startswith(".") \ No newline at end of file
diff --git a/hydroroll/config.py b/hydroroll/config.py
deleted file mode 100644
index 7cd7520..0000000
--- a/hydroroll/config.py
+++ /dev/null
@@ -1,80 +0,0 @@
-import argparse
-import sys
-import platform
-from importlib.metadata import version
-import os
-from typing import Set, Optional
-from iamai import ConfigModel
-
-# 创建全局 ArgumentParser 对象
-global_parser = argparse.ArgumentParser(description="HydroRoll[水系] 全局命令参数")
-
-class BasePluginConfig(ConfigModel):
- __config_name__ = ""
- handle_all_message: bool = True
- """是否处理所有类型的消息,此配置为 True 时会覆盖 handle_friend_message 和 handle_group_message。"""
- handle_friend_message: bool = True
- """是否处理好友消息。"""
- handle_group_message: bool = True
- """是否处理群消息。"""
- accept_group: Optional[Set[int]] = None
- """处理消息的群号,仅当 handle_group_message 为 True 时生效,留空表示处理所有群。"""
- message_str: str = "*{user_name} {message}"
- """最终发送消息的格式。"""
-
-
-class RegexPluginConfig(BasePluginConfig):
- pass
-
-
-class CommandPluginConfig(RegexPluginConfig):
- command_prefix: Set[str] = {":", "你妈", "👅", "约瑟夫妥斯妥耶夫斯基戴安那只鸡🐔"}
- """命令前缀。"""
- command: Set[str] = {}
- """命令文本。"""
- ignore_case: bool = True
- """忽略大小写。"""
-
-
-# 定义全局配置类
-class GlobalConfig(CommandPluginConfig):
- _name = "HydroRoll[水系]"
- _version = "0.1.0"
- _svn = "2"
- _author = "简律纯"
- _iamai_version = version("iamai")
- _python_ver = sys.version
- _python_ver_raw = ".".join(map(str, platform.python_version_tuple()[:3]))
- current_path = os.path.dirname(os.path.abspath("__file__"))
-
- # 定义系统组件
- class HydroSystem:
- def __init__(self):
- self.parser = argparse.ArgumentParser(
- description="HydroRoll[水系].system 系统命令参数"
- )
- self.subparsers = self.parser.add_subparsers()
- self.status_parser = self.subparsers.add_parser(
- "status", aliases=["stat", "state"], help="系统状态"
- )
- self.reload_parser = self.subparsers.add_parser(
- "reload", aliases=["rld"], help="重新加载系统"
- )
- self.restart_parser = self.subparsers.add_parser(
- "restart", aliases=["rst"], help="重启系统"
- )
- self.collect_parser = self.subparsers.add_parser(
- "collect", aliases=["gc"], help="释放 python 内存"
- )
- self.help = "\n".join(
- self.parser.format_help()
- .replace("\r\n", "\n")
- .replace("\r", "")
- .split("\n")[2:-3]
- )
-
- class HydroBot:
- def __init__(self) -> None:
- self.parser = argparse.ArgumentParser(description="Bot命令")
-
-
diff --git a/hydroroll/core/__init__.py b/hydroroll/core/__init__.py
deleted file mode 100644
index ed02489..0000000
--- a/hydroroll/core/__init__.py
+++ /dev/null
@@ -1,70 +0,0 @@
-from abc import ABC, abstractmethod
-import os, 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 ...HydroRoll.typing import T_Event, T_Config
-
-if TYPE_CHECKING:
- from iamai.bot import Bot
-
-class RuleLoadType(Enum):
- """插件加载类型。"""
-
- DIR = "dir"
- NAME = "name"
- FILE = "file"
- CLASS = "class"
-
-
-class Rule(ABC, Generic[T_Event, T_Config]):
- """所有 iamai 插件的基类。
-
- Attributes:
- event: 当前正在被此插件处理的事件。
- priority: 插件的优先级,数字越小表示优先级越高,默认为 0。
- block: 插件执行结束后是否阻止事件的传播。True 表示阻止。
- __rule_load_type__: 插件加载类型,由 iamai 自动设置,反映了此插件是如何被加载的。
- __rule_file_path__: 当插件加载类型为 `RuleLoadType.CLASS` 时为 `None`,
- 否则为定义插件在的 Python 模块的位置。
- """
-
- event: T_Event
- priority: int = 0
- Config: Type[ConfigModel]
-
- __rule_load_type__: RuleLoadType
- __rule_file_path__: Optional[str]
-
- def __init__(self, event: T_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__()` 方法调用。"""
- pass
-
- @property
- def name(self) -> str:
- """规则类名称。"""
- return self.__class__.__name__
-
- @property
- def bot(self) -> "Bot":
- """机器人对象。"""
- return self.event.adapter.bot
-
- @property
- def config(self) -> Optional[T_Config]:
- """规则包配置。"""
- 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 \ No newline at end of file
diff --git a/hydroroll/exceptions.py b/hydroroll/exceptions.py
deleted file mode 100644
index e69de29..0000000
--- a/hydroroll/exceptions.py
+++ /dev/null
diff --git a/hydroroll/psi/.gitkeep b/hydroroll/psi/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/hydroroll/psi/.gitkeep
+++ /dev/null
diff --git a/hydroroll/typing.py b/hydroroll/typing.py
deleted file mode 100644
index 876fa92..0000000
--- a/hydroroll/typing.py
+++ /dev/null
@@ -1,34 +0,0 @@
-"""HydroRoll 类型提示支持。
-
-此模块定义了部分 HydroRoll 使用的类型。
-"""
-
-from typing import TYPE_CHECKING, TypeVar, Callable, NoReturn, Awaitable
-
-from iamai.message import T_MS, T_Message, T_MessageSegment
-
-if TYPE_CHECKING:
- from iamai.bot import Bot # noqa
- from iamai.event import Event # noqa
- from iamai.plugin import Plugin # noqa
- from iamai.config import ConfigModel # noqa
-
-__all__ = [
- "T_State",
- "T_Event",
- "T_Plugin",
- "T_Config",
- "T_Message",
- "T_MessageSegment",
- "T_MS",
- "T_BotHook",
- "T_EventHook",
-]
-
-T_State = TypeVar("T_State")
-T_Event = TypeVar("T_Event", bound="Event")
-T_Plugin = TypeVar("T_Plugin", bound="Plugin")
-T_Config = TypeVar("T_Config", bound="ConfigModel")
-
-T_BotHook = Callable[["Bot"], Awaitable[NoReturn]]
-T_EventHook = Callable[[T_Event], Awaitable[NoReturn]] \ No newline at end of file
diff --git a/hydroroll/utils.py b/hydroroll/utils.py
deleted file mode 100644
index b4dbab6..0000000
--- a/hydroroll/utils.py
+++ /dev/null
@@ -1,97 +0,0 @@
-import re
-from abc import ABC, abstractmethod
-from typing import Type, Union, Generic, TypeVar
-from iamai import Plugin
-from iamai.typing import T_State
-from iamai.adapter.cqhttp.event import GroupMessageEvent, PrivateMessageEvent
-
-from .config import BasePluginConfig, RegexPluginConfig, CommandPluginConfig
-
-T_Config = TypeVar("T_Config", bound=BasePluginConfig)
-T_RegexPluginConfig = TypeVar("T_RegexPluginConfig", bound=RegexPluginConfig)
-T_CommandPluginConfig = TypeVar("T_CommandPluginConfig", bound=CommandPluginConfig)
-
-class BasePlugin(
- Plugin[Union[PrivateMessageEvent, GroupMessageEvent], T_State, T_Config],
- ABC,
- Generic[T_State, T_Config],
-):
- Config: Type[T_Config] = BasePluginConfig
-
- def format_str(self, format_str: str, message_str: str = "") -> str:
- return format_str.format(
- message=message_str,
- user_name=self.event.sender.nickname,
- user_id=self.event.sender.user_id,
- )
-
- async def rule(self) -> bool:
- is_bot_off = False
-
- if self.event.adapter.name != "cqhttp":
- return False
- if self.event.type != "message":
- return False
- match_str = self.event.message.get_plain_text()
- if is_bot_off:
- if self.event.message.startswith(f'[CQ:at,qq={self.event.self_id}]'):
- match_str = re.sub(fr'^\[CQ:at,qq={self.event.self_id}\]', '', match_str)
- elif self.event.message.startswith(f'[CQ:at,qq={self.event.self_tiny_id}]'):
- match_str = re.sub(fr'^\[CQ:at,qq={self.event.self_tiny_id}\]', '', match_str)
- else:
- return False
- if self.config.handle_all_message:
- return self.str_match(match_str)
- elif self.config.handle_friend_message:
- if self.event.message_type == "private":
- return self.str_match(match_str)
- elif self.config.handle_group_message:
- if self.event.message_type == "group":
- if (
- self.config.accept_group is None
- or self.event.group_id in self.config.accept_group
- ):
- return self.str_match(match_str)
- elif self.config.handle_group_message:
- if self.event.message_type == "guild":
- return self.str_match(match_str)
- return False
-
- @abstractmethod
- def str_match(self, msg_str: str) -> bool:
- raise NotImplemented
-
-
-class RegexPluginBase(BasePlugin[T_State, T_RegexPluginConfig], ABC):
- msg_match: re.Match
- re_pattern: re.Pattern
- Config: Type[T_RegexPluginConfig] = RegexPluginConfig
-
- def str_match(self, msg_str: str) -> bool:
- msg_str = msg_str.strip()
- self.msg_match = self.re_pattern.fullmatch(msg_str)
- return bool(self.msg_match)
-
-
-class CommandPluginBase(RegexPluginBase[T_State, T_CommandPluginConfig], ABC):
- command_match: re.Match
- command_re_pattern: re.Pattern
- Config: Type[T_CommandPluginConfig] = CommandPluginConfig
-
- def str_match(self, msg_str: str) -> bool:
- if not hasattr(self, "command_re_pattern"):
- self.command_re_pattern = re.compile(
- f'({"|".join(self.config.command_prefix)})'
- f'({"|".join(self.config.command)})'
- r"\s*(?P<command_args>.*)",
- flags=re.I if self.config.ignore_case else 0,
- )
- msg_str = msg_str.strip()
- self.command_match = self.command_re_pattern.fullmatch(msg_str)
- if not self.command_match:
- return False
- self.msg_match = self.re_pattern.fullmatch(
- self.command_match.group("command_args")
- )
- return bool(self.msg_match)
-