aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--hydroroll/__init__.py223
-rw-r--r--hydroroll/bot.py50
-rw-r--r--hydroroll/config.py75
-rw-r--r--hydroroll/core/__init__.py70
-rw-r--r--hydroroll/exceptions.py0
-rw-r--r--hydroroll/plugins/plugin_base/config.py30
-rw-r--r--hydroroll/plugins/plugin_bot/__init__.py30
-rw-r--r--hydroroll/plugins/plugin_bot/config.py11
-rw-r--r--hydroroll/plugins/plugin_echo/__init__.py17
-rw-r--r--hydroroll/plugins/plugin_echo/config.py11
-rw-r--r--hydroroll/plugins/plugin_luck/__init__.py21
-rw-r--r--hydroroll/plugins/plugin_luck/config.py15
-rw-r--r--hydroroll/plugins/plugin_send/__init__.py28
-rw-r--r--hydroroll/plugins/plugin_send/config.py15
-rw-r--r--hydroroll/plugins/plugin_system/__init__.py75
-rw-r--r--hydroroll/plugins/plugin_system/config.py11
-rw-r--r--hydroroll/psi/.gitkeep0
-rw-r--r--hydroroll/typing.py34
-rw-r--r--hydroroll/utils.py (renamed from hydroroll/plugins/plugin_base/__init__.py)14
19 files changed, 393 insertions, 337 deletions
diff --git a/hydroroll/__init__.py b/hydroroll/__init__.py
index c72c991..b064bca 100644
--- a/hydroroll/__init__.py
+++ b/hydroroll/__init__.py
@@ -1,4 +1,221 @@
-name = "hydroroll"
+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
-from hydroroll.bot import Bot
-from hydroroll.config import GlobalConfig \ No newline at end of file
+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/bot.py b/hydroroll/bot.py
deleted file mode 100644
index cbfc07f..0000000
--- a/hydroroll/bot.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from iamai import Bot as _Bot
-from typing import Optional, Dict, List, Type, Any, Union
-from pathlib import Path
-import os
-from hydroroll.config import GlobalConfig
-
-current_dir = Path.cwd()
-script_file = current_dir.resolve() / __file__
-script_dir = script_file.parent
-
-__all__ = ["Bot"]
-
-class Bot:
- def __init__(
- self,
- *,
- config_file: Optional[str] = "config.toml",
- config_dict: Optional[Dict] = None,
- hot_reload: bool = False,
- ) -> None:
- self.bot = _Bot(hot_reload=hot_reload,
- config_file=config_file,
- config_dict=config_dict
- )
- self.bot.load_plugins_from_dirs(Path(f"{script_dir}/plugins"))
- self.create_folder_structure(GlobalConfig._folder_dict)
-
- def run(self) -> None:
- self.bot.run()
-
- def restart(self) -> None:
- self.bot.restart()
-
- def create_folders(self):
- folder_path = os.path.dirname(os.path.abspath('__file__')) # 获取main.py所在文件夹路径
- if not os.path.isdir(os.path.join(folder_path, 'user')):
- os.mkdir(os.path.join(folder_path, 'user'))
- if not os.path.isdir(os.path.join(folder_path, 'data')):
- os.mkdir(os.path.join(folder_path, 'data'))
- if not os.path.isdir(os.path.join(folder_path, 'models')):
- os.mkdir(os.path.join(folder_path, 'models'))
- if not os.path.isdir(os.path.join(folder_path, 'web')):
- os.mkdir(os.path.join(folder_path, 'web'))
- if not os.path.isdir(os.path.join(folder_path, 'config')):
- os.mkdir(os.path.join(folder_path, 'config'))
- if not os.path.isdir(os.path.join(folder_path, 'logs')):
- os.mkdir(os.path.join(folder_path, 'logs'))
- if not os.path.isdir(os.path.join(folder_path, 'rules')):
- os.mkdir(os.path.join(folder_path, 'rules'))
-
diff --git a/hydroroll/config.py b/hydroroll/config.py
index 4258606..7cd7520 100644
--- a/hydroroll/config.py
+++ b/hydroroll/config.py
@@ -2,34 +2,79 @@ import argparse
import sys
import platform
from importlib.metadata import version
-from iamai import Plugin
import os
+from typing import Set, Optional
+from iamai import ConfigModel
# 创建全局 ArgumentParser 对象
-global_parser = argparse.ArgumentParser(description='hydroroll[水系] 全局命令参数')
+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:
- _name = "hydroroll"
+class GlobalConfig(CommandPluginConfig):
+ _name = "HydroRoll[水系]"
_version = "0.1.0"
_svn = "2"
_author = "简律纯"
- _iamai_version = version('iamai')
+ _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__'))
- _folders = {'config':{},'data':{},'logs':{},'models':{},'rules':{'rules_default'},'users':{},'web':{'frontend':{'static','js','css','public'},'backend':{'app','template'}}}
-
+ _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.parser = argparse.ArgumentParser(
+ description="HydroRoll[水系].system 系统命令参数"
+ )
self.subparsers = self.parser.add_subparsers()
- self.status_parser = self.subparsers.add_parser('status', aliases=['s'], help='系统状态')
- self.reload_parser = self.subparsers.add_parser('reload', aliases=['rld'], help='重新加载系统')
- self.restart_parser = self.subparsers.add_parser('restart', aliases=['rst'], help='重启系统')
- self.help = '\n'.join(self.parser.format_help().replace('\r\n', '\n').replace('\r', '').split('\n')[2:-3])
+ 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命令")
- \ No newline at end of file
+
+
diff --git a/hydroroll/core/__init__.py b/hydroroll/core/__init__.py
new file mode 100644
index 0000000..ed02489
--- /dev/null
+++ b/hydroroll/core/__init__.py
@@ -0,0 +1,70 @@
+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
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/hydroroll/exceptions.py
diff --git a/hydroroll/plugins/plugin_base/config.py b/hydroroll/plugins/plugin_base/config.py
deleted file mode 100644
index 311874c..0000000
--- a/hydroroll/plugins/plugin_base/config.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from typing import Set, Optional
-
-from iamai import ConfigModel
-
-
-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
- """忽略大小写。"""
diff --git a/hydroroll/plugins/plugin_bot/__init__.py b/hydroroll/plugins/plugin_bot/__init__.py
deleted file mode 100644
index 89ba17b..0000000
--- a/hydroroll/plugins/plugin_bot/__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-import re
-from importlib.metadata import version
-from plugins.plugin_base import CommandPluginBase
-from hydroroll.config import GlobalConfig
-
-from .config import Config
-
-
-class HydroBot(CommandPluginBase[None, Config]):
- Config = Config
- CurrentConfig = GlobalConfig
- priority = 0
-
- def __post_init__(self):
- self.re_pattern = re.compile(r"(?P<bot_info_str>.*)", flags=re.I)
-
- def bot_info(self):
- info_str = f'{self.CurrentConfig._name} '\
- f'{self.CurrentConfig._version}({self.CurrentConfig._svn}) '\
- f'by {self.CurrentConfig._author} '\
- f'on Python {self.CurrentConfig._python_ver_raw} '\
- f'with {" & ".join([adapter + "("+version("iamai-adapter-"+adapter) +")" for adapter in dict(self.bot.config.adapter)])} '\
- f'for iamai({self.CurrentConfig._iamai_version})'
-
- return info_str
-
- async def handle(self) -> None:
- await self.event.reply(
- self.format_str(self.config.message_str, self.bot_info())
- )
diff --git a/hydroroll/plugins/plugin_bot/config.py b/hydroroll/plugins/plugin_bot/config.py
deleted file mode 100644
index 640f23a..0000000
--- a/hydroroll/plugins/plugin_bot/config.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from typing import Set
-
-from plugins.plugin_base import CommandPluginConfig
-
-
-class Config(CommandPluginConfig):
- __config_name__ = "plugin_bot_info"
- command: Set[str] = {"bot"}
- """命令文本。"""
- message_str: str = "{message}"
- """最终发送消息的格式。"""
diff --git a/hydroroll/plugins/plugin_echo/__init__.py b/hydroroll/plugins/plugin_echo/__init__.py
deleted file mode 100644
index faa47df..0000000
--- a/hydroroll/plugins/plugin_echo/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import re
-
-from plugins.plugin_base import CommandPluginBase
-
-from .config import Config
-
-
-class Echo(CommandPluginBase[None, Config]):
- Config = Config
-
- def __post_init__(self):
- self.re_pattern = re.compile(r"(?P<echo_str>.*)", flags=re.I)
-
- async def handle(self) -> None:
- await self.event.reply(
- self.format_str(self.config.message_str, self.msg_match.group("echo_str"))
- )
diff --git a/hydroroll/plugins/plugin_echo/config.py b/hydroroll/plugins/plugin_echo/config.py
deleted file mode 100644
index c047134..0000000
--- a/hydroroll/plugins/plugin_echo/config.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from typing import Set
-
-from plugins.plugin_base import CommandPluginConfig
-
-
-class Config(CommandPluginConfig):
- __config_name__ = "plugin_echo"
- command: Set[str] = {"echo"}
- """命令文本。"""
- message_str: str = "*{user_name} {message}"
- """最终发送消息的格式。"""
diff --git a/hydroroll/plugins/plugin_luck/__init__.py b/hydroroll/plugins/plugin_luck/__init__.py
deleted file mode 100644
index 7967a0b..0000000
--- a/hydroroll/plugins/plugin_luck/__init__.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import re
-import time
-import random
-
-from plugins.plugin_base import CommandPluginBase
-
-from .config import Config
-
-
-class Luck(CommandPluginBase[None, Config]):
- Config = Config
-
- def __post_init__(self):
- self.re_pattern = re.compile(r".*", flags=re.I)
-
- async def handle(self) -> None:
- random.seed(
- time.strftime("%Y%j", time.localtime()) + self.format_str("{user_id}")
- )
- lucy = random.randint(self.config.min_int, self.config.max_int)
- await self.event.reply(self.format_str(self.config.message_str, str(lucy)))
diff --git a/hydroroll/plugins/plugin_luck/config.py b/hydroroll/plugins/plugin_luck/config.py
deleted file mode 100644
index 6190531..0000000
--- a/hydroroll/plugins/plugin_luck/config.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from typing import Set
-
-from plugins.plugin_base import CommandPluginConfig
-
-
-class Config(CommandPluginConfig):
- __config_name__ = "plugin_luck"
- command: Set[str] = {"luck"}
- """命令文本。"""
- min_int: int = 0
- """最小随机整数。"""
- max_int: int = 100
- """最大随机整数。"""
- message_str: str = "{user_name}今天的运气是: {message}"
- """最终发送消息的格式。"""
diff --git a/hydroroll/plugins/plugin_send/__init__.py b/hydroroll/plugins/plugin_send/__init__.py
deleted file mode 100644
index 2468189..0000000
--- a/hydroroll/plugins/plugin_send/__init__.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import re
-
-from plugins.plugin_base import CommandPluginBase
-
-from .config import Config
-
-
-class Send(CommandPluginBase[None, Config]):
- Config = Config
-
- def __post_init__(self):
- self.re_pattern = re.compile(r"\s*(?P<message>.*)", flags=re.I)
-
- async def handle(self) -> None:
- try:
- await self.event.adapter.send(
- self.msg_match.group("message"),
- "private",
- self.config.send_user_id,
- )
- except Exception as e:
- if self.config.send_filed_msg is not None:
- await self.event.reply(
- self.format_str(self.config.send_filed_msg, repr(e))
- )
- else:
- if self.config.send_success_msg is not None:
- await self.event.reply(self.format_str(self.config.send_success_msg))
diff --git a/hydroroll/plugins/plugin_send/config.py b/hydroroll/plugins/plugin_send/config.py
deleted file mode 100644
index 5b7fe7b..0000000
--- a/hydroroll/plugins/plugin_send/config.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from typing import Set, Optional
-
-from plugins.plugin_base import CommandPluginConfig
-
-
-class Config(CommandPluginConfig):
- __config_name__ = "plugin_send"
- command: Set[str] = {"send"}
- """命令文本。"""
- send_user_id: int = 2753364619
- """发送消息的对象的 QQ 号码。"""
- send_success_msg: Optional[str] = "已将消息送出√"
- """发送成功时回复的消息,设置为 None 表示不发送任何消息。"""
- send_filed_msg: Optional[str] = "发送失败:{message}"
- """发送失败时回复的消息。"""
diff --git a/hydroroll/plugins/plugin_system/__init__.py b/hydroroll/plugins/plugin_system/__init__.py
deleted file mode 100644
index ea797d3..0000000
--- a/hydroroll/plugins/plugin_system/__init__.py
+++ /dev/null
@@ -1,75 +0,0 @@
-import re
-from plugins.plugin_base import CommandPluginBase
-from .config import Config
-import psutil
-import time
-from hydroroll.config import GlobalConfig
-from iamai.adapter.cqhttp.message import CQHTTPMessageSegment
-
-class System(CommandPluginBase[None, Config]):
- priority: int = 0
- block: bool = True
- Config = Config
- CurrentConfig = GlobalConfig
-
- def __post_init__(self):
- self.re_pattern = re.compile(r"(?P<system_info_str>.*)", flags=re.I)
-
- def eventReply(self, message: str):
- return self.event.reply(
- self.format_str(self.config.message_str, message)
- )
-
- def get_system_status(self) -> str:
- cpu_usage = psutil.cpu_percent()
- memory_usage = psutil.virtual_memory().percent
- disk_usage = psutil.disk_usage('/').percent
-
- current_time = time.time()
- start_time = psutil.Process().create_time()
-
- uptime_seconds = int(current_time - start_time)
- uptime_str = time.strftime("%H:%M:%S", time.gmtime(uptime_seconds))
-
- info_str = f"{self.CurrentConfig._name} Ver.{self.CurrentConfig._version}"
- info_str += f"({self.CurrentConfig._svn}) built in Python {self.CurrentConfig._python_ver}\n"
- info_str += f"本地时间: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}\n"
- info_str += f"已启动时间:{uptime_str}\n"
- info_str += f"CPU使用率:{cpu_usage}%\n"
- info_str += f"内存占用率:{memory_usage}%\n"
- info_str += f"磁盘使用率:{disk_usage}%"
-
- return info_str
-
- async def handle(self) -> None:
- system = self.CurrentConfig.HydroSystem()
- try:
- sub_command = self.event.get_plain_text().split()[1]
- except IndexError:
- sub_command = ""
-
- if sub_command in ["status", "s"]:
- await self.event.reply(
- self.format_str(self.config.message_str,
- self.get_system_status())
- )
- elif sub_command in ["restart", "rst"]:
- await self.event.reply(
- self.format_str(self.config.message_str, "正在重启系统...")
- )
- self.bot.restart()
-
- elif sub_command in ["reload", "rld"]:
- await self.event.reply(
- self.format_str(self.config.message_str, "正在重载...")
- )
- self.bot.reload_plugins()
- await self.event.reply(
- self.format_str(self.config.message_str,
- f"已加载{len(self.bot.plugins)}枚插件")
- )
- else:
- await self.event.reply(
- CQHTTPMessageSegment.reply(self.event.message_id) +
- self.format_str(self.config.message_str, system.help)
- )
diff --git a/hydroroll/plugins/plugin_system/config.py b/hydroroll/plugins/plugin_system/config.py
deleted file mode 100644
index aa5f51b..0000000
--- a/hydroroll/plugins/plugin_system/config.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from typing import Set
-
-from plugins.plugin_base import CommandPluginConfig
-
-
-class Config(CommandPluginConfig):
- __config_name__ = "plugin_system_info"
- command: Set[str] = {"system"}
- """命令文本。"""
- message_str: str = "{message}"
- """最终发送消息的格式。"""
diff --git a/hydroroll/psi/.gitkeep b/hydroroll/psi/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/hydroroll/psi/.gitkeep
diff --git a/hydroroll/typing.py b/hydroroll/typing.py
new file mode 100644
index 0000000..876fa92
--- /dev/null
+++ b/hydroroll/typing.py
@@ -0,0 +1,34 @@
+"""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/plugins/plugin_base/__init__.py b/hydroroll/utils.py
index a050ae1..b4dbab6 100644
--- a/hydroroll/plugins/plugin_base/__init__.py
+++ b/hydroroll/utils.py
@@ -1,7 +1,6 @@
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
@@ -12,7 +11,6 @@ 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,
@@ -28,7 +26,7 @@ class BasePlugin(
)
async def rule(self) -> bool:
- is_bot_off = True
+ is_bot_off = False
if self.event.adapter.name != "cqhttp":
return False
@@ -38,6 +36,8 @@ class BasePlugin(
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:
@@ -52,6 +52,9 @@ class BasePlugin(
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
@@ -78,7 +81,7 @@ class CommandPluginBase(RegexPluginBase[T_State, T_CommandPluginConfig], ABC):
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_prefix)})'
f'({"|".join(self.config.command)})'
r"\s*(?P<command_args>.*)",
flags=re.I if self.config.ignore_case else 0,
@@ -90,4 +93,5 @@ class CommandPluginBase(RegexPluginBase[T_State, T_CommandPluginConfig], ABC):
self.msg_match = self.re_pattern.fullmatch(
self.command_match.group("command_args")
)
- return bool(self.msg_match) \ No newline at end of file
+ return bool(self.msg_match)
+